Skip to main content

reth_trie_sparse/
parallel.rs

1use crate::{
2    lower::LowerSparseSubtrie,
3    provider::{RevealedNode, TrieNodeProvider},
4    LeafLookup, LeafLookupError, RlpNodeStackItem, SparseNode, SparseNodeType, SparseTrie,
5    SparseTrieUpdates,
6};
7use alloc::{borrow::Cow, boxed::Box, vec, vec::Vec};
8use alloy_primitives::{
9    map::{Entry, HashMap},
10    B256, U256,
11};
12use alloy_rlp::Decodable;
13use alloy_trie::{BranchNodeCompact, TrieMask, EMPTY_ROOT_HASH};
14use core::cmp::{Ord, Ordering, PartialOrd};
15use reth_execution_errors::{SparseTrieError, SparseTrieErrorKind, SparseTrieResult};
16use reth_trie_common::{
17    prefix_set::{PrefixSet, PrefixSetMut},
18    BranchNodeMasks, BranchNodeMasksMap, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles,
19    ProofTrieNode, RlpNode, TrieNode,
20};
21use smallvec::SmallVec;
22use tracing::{debug, instrument, trace};
23
24/// The maximum length of a path, in nibbles, which belongs to the upper subtrie of a
25/// [`ParallelSparseTrie`]. All longer paths belong to a lower subtrie.
26pub const UPPER_TRIE_MAX_DEPTH: usize = 2;
27
28/// Number of lower subtries which are managed by the [`ParallelSparseTrie`].
29pub const NUM_LOWER_SUBTRIES: usize = 16usize.pow(UPPER_TRIE_MAX_DEPTH as u32);
30
31/// Configuration for controlling when parallelism is enabled in [`ParallelSparseTrie`] operations.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub struct ParallelismThresholds {
34    /// Minimum number of nodes to reveal before parallel processing is enabled.
35    /// When `reveal_nodes` has fewer nodes than this threshold, they will be processed serially.
36    pub min_revealed_nodes: usize,
37    /// Minimum number of changed keys (prefix set length) before parallel processing is enabled
38    /// for hash updates. When updating subtrie hashes with fewer changed keys than this threshold,
39    /// the updates will be processed serially.
40    pub min_updated_nodes: usize,
41}
42
43/// A revealed sparse trie with subtries that can be updated in parallel.
44///
45/// ## Structure
46///
47/// The trie is divided into two tiers for efficient parallel processing:
48/// - **Upper subtrie**: Contains nodes with paths shorter than [`UPPER_TRIE_MAX_DEPTH`]
49/// - **Lower subtries**: An array of [`NUM_LOWER_SUBTRIES`] subtries, each handling nodes with
50///   paths of at least [`UPPER_TRIE_MAX_DEPTH`] nibbles
51///
52/// Node placement is determined by path depth:
53/// - Paths with < [`UPPER_TRIE_MAX_DEPTH`] nibbles go to the upper subtrie
54/// - Paths with >= [`UPPER_TRIE_MAX_DEPTH`] nibbles go to lower subtries, indexed by their first
55///   [`UPPER_TRIE_MAX_DEPTH`] nibbles.
56///
57/// Each lower subtrie tracks its root via the `path` field, which represents the shortest path
58/// in that subtrie. This path will have at least [`UPPER_TRIE_MAX_DEPTH`] nibbles, but may be
59/// longer when an extension node in the upper trie "reaches into" the lower subtrie. For example,
60/// if the upper trie has an extension from `0x1` to `0x12345`, then the lower subtrie for prefix
61/// `0x12` will have its root at path `0x12345` rather than at `0x12`.
62///
63/// ## Node Revealing
64///
65/// The trie uses lazy loading to efficiently handle large state tries. Nodes can be:
66/// - **Blind nodes**: Stored as hashes ([`SparseNode::Hash`]), representing unloaded trie parts
67/// - **Revealed nodes**: Fully loaded nodes (Branch, Extension, Leaf) with complete structure
68///
69/// Note: An empty trie contains an `EmptyRoot` node at the root path, rather than no nodes at all.
70/// A trie with no nodes is blinded, its root may be `EmptyRoot` or some other node type.
71///
72/// Revealing is generally done using pre-loaded node data provided to via `reveal_nodes`. In
73/// certain cases, such as edge-cases when updating/removing leaves, nodes are revealed on-demand.
74///
75/// ## Leaf Operations
76///
77/// **Update**: When updating a leaf, the new value is stored in the appropriate subtrie's values
78/// map. If the leaf is new, the trie structure is updated by walking to the leaf from the root,
79/// creating necessary intermediate branch nodes.
80///
81/// **Removal**: Leaf removal may require parent node modifications. The algorithm walks up the
82/// trie, removing nodes that become empty and converting single-child branches to extensions.
83///
84/// During leaf operations the overall structure of the trie may change, causing nodes to be moved
85/// from the upper to lower trie or vice-versa.
86///
87/// The `prefix_set` is modified during both leaf updates and removals to track changed leaf paths.
88///
89/// ## Root Hash Calculation
90///
91/// Root hash computation follows a bottom-up approach:
92/// 1. Update hashes for all modified lower subtries (can be done in parallel)
93/// 2. Update hashes for the upper subtrie (which may reference lower subtrie hashes)
94/// 3. Calculate the final root hash from the upper subtrie's root node
95///
96/// The `prefix_set` tracks which paths have been modified, enabling incremental updates instead of
97/// recalculating the entire trie.
98///
99/// ## Invariants
100///
101/// - Each leaf entry in the `subtries` and `upper_trie` collection must have a corresponding entry
102///   in `values` collection. If the root node is a leaf, it must also have an entry in `values`.
103/// - All keys in `values` collection are full leaf paths.
104#[derive(Clone, PartialEq, Eq, Debug)]
105pub struct ParallelSparseTrie {
106    /// This contains the trie nodes for the upper part of the trie.
107    upper_subtrie: Box<SparseSubtrie>,
108    /// An array containing the subtries at the second level of the trie.
109    lower_subtries: Box<[LowerSparseSubtrie; NUM_LOWER_SUBTRIES]>,
110    /// Set of prefixes (key paths) that have been marked as updated.
111    /// This is used to track which parts of the trie need to be recalculated.
112    prefix_set: PrefixSetMut,
113    /// Optional tracking of trie updates for later use.
114    updates: Option<SparseTrieUpdates>,
115    /// Branch node masks containing `tree_mask` and `hash_mask` for each path.
116    /// - `tree_mask`: When a bit is set, the corresponding child subtree is stored in the
117    ///   database.
118    /// - `hash_mask`: When a bit is set, the corresponding child is stored as a hash in the
119    ///   database.
120    branch_node_masks: BranchNodeMasksMap,
121    /// Reusable buffer pool used for collecting [`SparseTrieUpdatesAction`]s during hash
122    /// computations.
123    update_actions_buffers: Vec<Vec<SparseTrieUpdatesAction>>,
124    /// Thresholds controlling when parallelism is enabled for different operations.
125    parallelism_thresholds: ParallelismThresholds,
126    /// Tracks heat of lower subtries for smart pruning decisions.
127    /// Hot subtries are skipped during pruning to keep frequently-used data revealed.
128    subtrie_heat: SubtrieModifications,
129    /// Metrics for the parallel sparse trie.
130    #[cfg(feature = "metrics")]
131    metrics: crate::metrics::ParallelSparseTrieMetrics,
132}
133
134impl Default for ParallelSparseTrie {
135    fn default() -> Self {
136        Self {
137            upper_subtrie: Box::new(SparseSubtrie {
138                nodes: HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]),
139                ..Default::default()
140            }),
141            lower_subtries: Box::new(
142                [const { LowerSparseSubtrie::Blind(None) }; NUM_LOWER_SUBTRIES],
143            ),
144            prefix_set: PrefixSetMut::default(),
145            updates: None,
146            branch_node_masks: BranchNodeMasksMap::default(),
147            update_actions_buffers: Vec::default(),
148            parallelism_thresholds: Default::default(),
149            subtrie_heat: SubtrieModifications::default(),
150            #[cfg(feature = "metrics")]
151            metrics: Default::default(),
152        }
153    }
154}
155
156impl SparseTrie for ParallelSparseTrie {
157    fn set_root(
158        &mut self,
159        root: TrieNode,
160        masks: Option<BranchNodeMasks>,
161        retain_updates: bool,
162    ) -> SparseTrieResult<()> {
163        // A fresh/cleared `ParallelSparseTrie` has a `SparseNode::Empty` at its root in the upper
164        // subtrie. Delete that so we can reveal the new root node.
165        let path = Nibbles::default();
166        let _removed_root = self.upper_subtrie.nodes.remove(&path).expect("root node should exist");
167        debug_assert_eq!(_removed_root, SparseNode::Empty);
168
169        self.set_updates(retain_updates);
170
171        self.reveal_upper_node(Nibbles::default(), &root, masks)
172    }
173
174    fn set_updates(&mut self, retain_updates: bool) {
175        self.updates = retain_updates.then(Default::default);
176    }
177
178    fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNode]) -> SparseTrieResult<()> {
179        if nodes.is_empty() {
180            return Ok(())
181        }
182
183        // Sort nodes first by their subtrie, and secondarily by their path. This allows for
184        // grouping nodes by their subtrie using `chunk_by`.
185        nodes.sort_unstable_by(
186            |ProofTrieNode { path: path_a, .. }, ProofTrieNode { path: path_b, .. }| {
187                let subtrie_type_a = SparseSubtrieType::from_path(path_a);
188                let subtrie_type_b = SparseSubtrieType::from_path(path_b);
189                subtrie_type_a.cmp(&subtrie_type_b).then_with(|| path_a.cmp(path_b))
190            },
191        );
192
193        // Update the top-level branch node masks. This is simple and can't be done in parallel.
194        self.branch_node_masks.reserve(nodes.len());
195        for ProofTrieNode { path, masks, .. } in nodes.iter() {
196            if let Some(branch_masks) = masks {
197                self.branch_node_masks.insert(*path, *branch_masks);
198            }
199        }
200
201        // Due to the sorting all upper subtrie nodes will be at the front of the slice. We split
202        // them off from the rest to be handled specially by
203        // `ParallelSparseTrie::reveal_upper_node`.
204        let num_upper_nodes = nodes
205            .iter()
206            .position(|n| !SparseSubtrieType::path_len_is_upper(n.path.len()))
207            .unwrap_or(nodes.len());
208        let (upper_nodes, lower_nodes) = nodes.split_at(num_upper_nodes);
209
210        // Reserve the capacity of the upper subtrie's `nodes` HashMap before iterating, so we don't
211        // end up making many small capacity changes as we loop.
212        self.upper_subtrie.nodes.reserve(upper_nodes.len());
213        for node in upper_nodes {
214            self.reveal_upper_node(node.path, &node.node, node.masks)?;
215        }
216
217        let reachable_subtries = self.reachable_subtries();
218
219        if !self.is_reveal_parallelism_enabled(lower_nodes.len()) {
220            for node in lower_nodes {
221                let idx = path_subtrie_index_unchecked(&node.path);
222                if !reachable_subtries.get(idx) {
223                    trace!(
224                        target: "trie::parallel_sparse",
225                        reveal_path = ?node.path,
226                        "Node's lower subtrie is not reachable, skipping",
227                    );
228                    continue;
229                }
230                // For boundary leaves, check reachability from upper subtrie's parent branch
231                if node.path.len() == UPPER_TRIE_MAX_DEPTH &&
232                    !Self::is_boundary_leaf_reachable(
233                        &self.upper_subtrie.nodes,
234                        &node.path,
235                        &node.node,
236                    )
237                {
238                    trace!(
239                        target: "trie::parallel_sparse",
240                        path = ?node.path,
241                        "Boundary leaf not reachable from upper subtrie, skipping",
242                    );
243                    continue;
244                }
245                self.lower_subtries[idx].reveal(&node.path);
246                self.subtrie_heat.mark_modified(idx);
247                self.lower_subtries[idx]
248                    .as_revealed_mut()
249                    .expect("just revealed")
250                    .reveal_node(node.path, &node.node, node.masks)?;
251            }
252            return Ok(())
253        }
254
255        #[cfg(not(feature = "std"))]
256        unreachable!("nostd is checked by is_reveal_parallelism_enabled");
257
258        #[cfg(feature = "std")]
259        // Reveal lower subtrie nodes in parallel
260        {
261            use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
262            use tracing::Span;
263
264            // Capture the current span so it can be propagated to rayon worker threads
265            let parent_span = Span::current();
266
267            // Capture reference to upper subtrie nodes for boundary leaf reachability checks
268            let upper_nodes = &self.upper_subtrie.nodes;
269
270            // Group the nodes by lower subtrie. This must be collected into a Vec in order for
271            // rayon's `zip` to be happy.
272            let node_groups: Vec<_> = lower_nodes
273                .chunk_by(|node_a, node_b| {
274                    SparseSubtrieType::from_path(&node_a.path) ==
275                        SparseSubtrieType::from_path(&node_b.path)
276                })
277                .collect();
278
279            // Take the lower subtries in the same order that the nodes were grouped into, so that
280            // the two can be zipped together. This also must be collected into a Vec for rayon's
281            // `zip` to be happy.
282            let lower_subtries: Vec<_> = node_groups
283                .iter()
284                .filter_map(|nodes| {
285                    // NOTE: chunk_by won't produce empty groups
286                    let node = &nodes[0];
287                    let idx =
288                        SparseSubtrieType::from_path(&node.path).lower_index().unwrap_or_else(
289                            || panic!("upper subtrie node {node:?} found amongst lower nodes"),
290                        );
291
292                    if !reachable_subtries.get(idx) {
293                        trace!(
294                            target: "trie::parallel_sparse",
295                            nodes = ?nodes,
296                            "Lower subtrie is not reachable, skipping reveal",
297                        );
298                        return None;
299                    }
300
301                    // due to the nodes being sorted secondarily on their path, and chunk_by keeping
302                    // the first element of each group, the `path` here will necessarily be the
303                    // shortest path being revealed for each subtrie. Therefore we can reveal the
304                    // subtrie itself using this path and retain correct behavior.
305                    self.lower_subtries[idx].reveal(&node.path);
306                    Some((idx, self.lower_subtries[idx].take_revealed().expect("just revealed")))
307                })
308                .collect();
309
310            // Zip the lower subtries and their corresponding node groups, and reveal lower subtrie
311            // nodes in parallel
312            let results: Vec<_> = lower_subtries
313                .into_par_iter()
314                .zip(node_groups.into_par_iter())
315                .map(|((subtrie_idx, mut subtrie), nodes)| {
316                    // Enter the parent span to propagate context (e.g., hashed_address for storage
317                    // tries) to the worker thread
318                    let _guard = parent_span.enter();
319
320                    // reserve space in the HashMap ahead of time; doing it on a node-by-node basis
321                    // can cause multiple re-allocations as the hashmap grows.
322                    subtrie.nodes.reserve(nodes.len());
323
324                    for node in nodes {
325                        // For boundary leaves, check reachability from upper subtrie's parent
326                        // branch
327                        if node.path.len() == UPPER_TRIE_MAX_DEPTH &&
328                            !Self::is_boundary_leaf_reachable(
329                                upper_nodes,
330                                &node.path,
331                                &node.node,
332                            )
333                        {
334                            trace!(
335                                target: "trie::parallel_sparse",
336                                path = ?node.path,
337                                "Boundary leaf not reachable from upper subtrie, skipping",
338                            );
339                            continue;
340                        }
341                        // Reveal each node in the subtrie, returning early on any errors
342                        let res = subtrie.reveal_node(node.path, &node.node, node.masks);
343                        if res.is_err() {
344                            return (subtrie_idx, subtrie, res.map(|_| ()))
345                        }
346                    }
347                    (subtrie_idx, subtrie, Ok(()))
348                })
349                .collect();
350
351            // Put subtries back which were processed in the rayon pool, collecting the last
352            // seen error in the process and returning that.
353            let mut any_err = Ok(());
354            for (subtrie_idx, subtrie, res) in results {
355                self.lower_subtries[subtrie_idx] = LowerSparseSubtrie::Revealed(subtrie);
356                if res.is_err() {
357                    any_err = res;
358                }
359            }
360
361            any_err
362        }
363    }
364
365    fn update_leaf<P: TrieNodeProvider>(
366        &mut self,
367        full_path: Nibbles,
368        value: Vec<u8>,
369        provider: P,
370    ) -> SparseTrieResult<()> {
371        trace!(
372            target: "trie::parallel_sparse",
373            ?full_path,
374            value_len = value.len(),
375            "Updating leaf",
376        );
377
378        // Check if the value already exists - if so, just update it (no structural changes needed)
379        if self.upper_subtrie.inner.values.contains_key(&full_path) {
380            self.prefix_set.insert(full_path);
381            self.upper_subtrie.inner.values.insert(full_path, value);
382            return Ok(());
383        }
384        // Also check lower subtries for existing value
385        if let Some(subtrie) = self.lower_subtrie_for_path(&full_path) &&
386            subtrie.inner.values.contains_key(&full_path)
387        {
388            self.prefix_set.insert(full_path);
389            self.lower_subtrie_for_path_mut(&full_path)
390                .expect("subtrie exists")
391                .inner
392                .values
393                .insert(full_path, value);
394            return Ok(());
395        }
396
397        let retain_updates = self.updates_enabled();
398
399        // Insert value into upper subtrie temporarily. We'll move it to the correct subtrie
400        // during traversal, or clean it up if we error.
401        self.upper_subtrie.inner.values.insert(full_path, value.clone());
402
403        // Start at the root, traversing until we find either the node to update or a subtrie to
404        // update.
405        //
406        // We first traverse the upper subtrie for two levels, and moving any created nodes to a
407        // lower subtrie if necessary.
408        //
409        // We use `next` to keep track of the next node that we need to traverse to, and
410        // `new_nodes` to keep track of any nodes that were created during the traversal.
411        let mut new_nodes = Vec::new();
412        let mut next = Some(Nibbles::default());
413        // Track the original node that was modified (path, original_node) for rollback
414        let mut modified_original: Option<(Nibbles, SparseNode)> = None;
415        // Track inserted branch masks for rollback
416        let mut inserted_masks: Vec<Nibbles> = Vec::new();
417
418        // Traverse the upper subtrie to find the node to update or the subtrie to update.
419        //
420        // We stop when the next node to traverse would be in a lower subtrie, or if there are no
421        // more nodes to traverse.
422        while let Some(current) =
423            next.filter(|next| SparseSubtrieType::path_len_is_upper(next.len()))
424        {
425            // Save original node for potential rollback (only if not already saved)
426            if modified_original.is_none() &&
427                let Some(node) = self.upper_subtrie.nodes.get(&current)
428            {
429                modified_original = Some((current, node.clone()));
430            }
431
432            // Traverse the next node, keeping track of any changed nodes and the next step in the
433            // trie. If traversal fails, clean up the value we inserted and propagate the error.
434            let step_result =
435                self.upper_subtrie.update_next_node(current, &full_path, retain_updates);
436
437            if step_result.is_err() {
438                self.upper_subtrie.inner.values.remove(&full_path);
439                return step_result.map(|_| ());
440            }
441
442            match step_result? {
443                LeafUpdateStep::Continue { next_node } => {
444                    next = Some(next_node);
445                    // Clear modified_original since we haven't actually modified anything yet
446                    modified_original = None;
447                }
448                LeafUpdateStep::Complete { inserted_nodes, reveal_path } => {
449                    new_nodes.extend(inserted_nodes);
450
451                    if let Some(reveal_path) = reveal_path {
452                        let subtrie = self.subtrie_for_path_mut(&reveal_path);
453                        let reveal_masks = if subtrie
454                            .nodes
455                            .get(&reveal_path)
456                            .expect("node must exist")
457                            .is_hash()
458                        {
459                            debug!(
460                                target: "trie::parallel_sparse",
461                                child_path = ?reveal_path,
462                                leaf_full_path = ?full_path,
463                                "Extension node child not revealed in update_leaf, falling back to db",
464                            );
465                            let revealed_node = match provider.trie_node(&reveal_path) {
466                                Ok(node) => node,
467                                Err(e) => {
468                                    self.rollback_insert(
469                                        &full_path,
470                                        &new_nodes,
471                                        &inserted_masks,
472                                        modified_original.take(),
473                                    );
474                                    return Err(e);
475                                }
476                            };
477                            if let Some(RevealedNode { node, tree_mask, hash_mask }) = revealed_node
478                            {
479                                let decoded = match TrieNode::decode(&mut &node[..]) {
480                                    Ok(d) => d,
481                                    Err(e) => {
482                                        self.rollback_insert(
483                                            &full_path,
484                                            &new_nodes,
485                                            &inserted_masks,
486                                            modified_original.take(),
487                                        );
488                                        return Err(e.into());
489                                    }
490                                };
491                                trace!(
492                                    target: "trie::parallel_sparse",
493                                    ?reveal_path,
494                                    ?decoded,
495                                    ?tree_mask,
496                                    ?hash_mask,
497                                    "Revealing child (from upper)",
498                                );
499                                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
500                                if let Err(e) = subtrie.reveal_node(reveal_path, &decoded, masks) {
501                                    self.rollback_insert(
502                                        &full_path,
503                                        &new_nodes,
504                                        &inserted_masks,
505                                        modified_original.take(),
506                                    );
507                                    return Err(e);
508                                }
509                                masks
510                            } else {
511                                self.rollback_insert(
512                                    &full_path,
513                                    &new_nodes,
514                                    &inserted_masks,
515                                    modified_original.take(),
516                                );
517                                return Err(SparseTrieErrorKind::NodeNotFoundInProvider {
518                                    path: reveal_path,
519                                }
520                                .into())
521                            }
522                        } else {
523                            None
524                        };
525
526                        if let Some(_masks) = reveal_masks {
527                            self.branch_node_masks.insert(reveal_path, _masks);
528                            inserted_masks.push(reveal_path);
529                        }
530                    }
531
532                    next = None;
533                }
534                LeafUpdateStep::NodeNotFound => {
535                    next = None;
536                }
537            }
538        }
539
540        // Move nodes from upper subtrie to lower subtries
541        for node_path in &new_nodes {
542            // Skip nodes that belong in the upper subtrie
543            if SparseSubtrieType::path_len_is_upper(node_path.len()) {
544                continue
545            }
546
547            let node =
548                self.upper_subtrie.nodes.remove(node_path).expect("node belongs to upper subtrie");
549
550            // If it's a leaf node, extract its value before getting mutable reference to subtrie.
551            let leaf_value = if let SparseNode::Leaf { key, .. } = &node {
552                let mut leaf_full_path = *node_path;
553                leaf_full_path.extend(key);
554                Some((
555                    leaf_full_path,
556                    self.upper_subtrie
557                        .inner
558                        .values
559                        .remove(&leaf_full_path)
560                        .expect("leaf nodes have associated values entries"),
561                ))
562            } else {
563                None
564            };
565
566            // Get or create the subtrie with the exact node path (not truncated to 2 nibbles).
567            let subtrie = self.subtrie_for_path_mut(node_path);
568
569            // Insert the leaf value if we have one
570            if let Some((leaf_full_path, value)) = leaf_value {
571                subtrie.inner.values.insert(leaf_full_path, value);
572            }
573
574            // Insert the node into the lower subtrie
575            subtrie.nodes.insert(*node_path, node);
576        }
577
578        // If we reached the max depth of the upper trie, we may have had more nodes to insert.
579        if let Some(next_path) = next.filter(|n| !SparseSubtrieType::path_len_is_upper(n.len())) {
580            // The value was inserted into the upper subtrie's `values` at the top of this method.
581            // At this point we know the value is not in the upper subtrie, and the call to
582            // `update_leaf` below will insert it into the lower subtrie. So remove it from the
583            // upper subtrie.
584            self.upper_subtrie.inner.values.remove(&full_path);
585
586            // Use subtrie_for_path to ensure the subtrie has the correct path.
587            //
588            // The next_path here represents where we need to continue traversal, which may
589            // be longer than 2 nibbles if we're following an extension node.
590            let subtrie = self.subtrie_for_path_mut(&next_path);
591
592            // Create an empty root at the subtrie path if the subtrie is empty
593            if subtrie.nodes.is_empty() {
594                subtrie.nodes.insert(subtrie.path, SparseNode::Empty);
595            }
596
597            // If we didn't update the target leaf, we need to call update_leaf on the subtrie
598            // to ensure that the leaf is updated correctly.
599            match subtrie.update_leaf(full_path, value, provider, retain_updates) {
600                Ok(Some((revealed_path, revealed_masks))) => {
601                    self.branch_node_masks.insert(revealed_path, revealed_masks);
602                }
603                Ok(None) => {}
604                Err(e) => {
605                    // Clean up: remove the value from lower subtrie if it was inserted
606                    if let Some(lower) = self.lower_subtrie_for_path_mut(&full_path) {
607                        lower.inner.values.remove(&full_path);
608                    }
609                    // Clean up any branch masks that were inserted during upper subtrie traversal
610                    for mask_path in &inserted_masks {
611                        self.branch_node_masks.remove(mask_path);
612                    }
613                    return Err(e);
614                }
615            }
616        }
617
618        // Insert into prefix_set only after all operations succeed
619        self.prefix_set.insert(full_path);
620
621        Ok(())
622    }
623
624    fn remove_leaf<P: TrieNodeProvider>(
625        &mut self,
626        full_path: &Nibbles,
627        provider: P,
628    ) -> SparseTrieResult<()> {
629        trace!(
630            target: "trie::parallel_sparse",
631            ?full_path,
632            "Removing leaf",
633        );
634
635        // When removing a leaf node it's possibly necessary to modify its parent node, and possibly
636        // the parent's parent node. It is not ever necessary to descend further than that; once an
637        // extension node is hit it must terminate in a branch or the root, which won't need further
638        // updates. So the situation with maximum updates is:
639        //
640        // - Leaf
641        // - Branch with 2 children, one being this leaf
642        // - Extension
643        //
644        // ...which will result in just a leaf or extension, depending on what the branch's other
645        // child is.
646        //
647        // Therefore, first traverse the trie in order to find the leaf node and at most its parent
648        // and grandparent.
649
650        let leaf_path;
651        let leaf_subtrie_type;
652
653        let mut branch_parent_path: Option<Nibbles> = None;
654        let mut branch_parent_node: Option<SparseNode> = None;
655
656        let mut ext_grandparent_path: Option<Nibbles> = None;
657        let mut ext_grandparent_node: Option<SparseNode> = None;
658
659        let mut curr_path = Nibbles::new(); // start traversal from root
660        let mut curr_subtrie_type = SparseSubtrieType::Upper;
661
662        // List of node paths which need to have their hashes reset
663        let mut paths_to_reset_hashes = Vec::new();
664
665        loop {
666            let curr_subtrie = match curr_subtrie_type {
667                SparseSubtrieType::Upper => &mut self.upper_subtrie,
668                SparseSubtrieType::Lower(idx) => {
669                    self.lower_subtries[idx].as_revealed_mut().expect("lower subtrie is revealed")
670                }
671            };
672            let curr_node = curr_subtrie.nodes.get_mut(&curr_path).unwrap();
673
674            match Self::find_next_to_leaf(&curr_path, curr_node, full_path) {
675                FindNextToLeafOutcome::NotFound => return Ok(()), // leaf isn't in the trie
676                FindNextToLeafOutcome::BlindedNode(hash) => {
677                    return Err(SparseTrieErrorKind::BlindedNode { path: curr_path, hash }.into())
678                }
679                FindNextToLeafOutcome::Found => {
680                    // this node is the target leaf
681                    leaf_path = curr_path;
682                    leaf_subtrie_type = curr_subtrie_type;
683                    break;
684                }
685                FindNextToLeafOutcome::ContinueFrom(next_path) => {
686                    // Any branches/extensions along the path to the leaf will have their `hash`
687                    // field unset, as it will no longer be valid once the leaf is removed.
688                    match curr_node {
689                        SparseNode::Branch { hash, .. } => {
690                            if hash.is_some() {
691                                paths_to_reset_hashes
692                                    .push((SparseSubtrieType::from_path(&curr_path), curr_path));
693                            }
694
695                            // If there is already an extension leading into a branch, then that
696                            // extension is no longer relevant.
697                            match (&branch_parent_path, &ext_grandparent_path) {
698                                (Some(branch), Some(ext)) if branch.len() > ext.len() => {
699                                    ext_grandparent_path = None;
700                                    ext_grandparent_node = None;
701                                }
702                                _ => (),
703                            };
704                            branch_parent_path = Some(curr_path);
705                            branch_parent_node = Some(curr_node.clone());
706                        }
707                        SparseNode::Extension { hash, .. } => {
708                            if hash.is_some() {
709                                paths_to_reset_hashes
710                                    .push((SparseSubtrieType::from_path(&curr_path), curr_path));
711                            }
712
713                            // We can assume a new branch node will be found after the extension, so
714                            // there's no need to modify branch_parent_path/node even if it's
715                            // already set.
716                            ext_grandparent_path = Some(curr_path);
717                            ext_grandparent_node = Some(curr_node.clone());
718                        }
719                        SparseNode::Empty | SparseNode::Hash(_) | SparseNode::Leaf { .. } => {
720                            unreachable!(
721                                "find_next_to_leaf only continues to a branch or extension"
722                            )
723                        }
724                    }
725
726                    curr_path = next_path;
727
728                    // Update subtrie type if we're crossing into the lower trie.
729                    let next_subtrie_type = SparseSubtrieType::from_path(&curr_path);
730                    if matches!(curr_subtrie_type, SparseSubtrieType::Upper) &&
731                        matches!(next_subtrie_type, SparseSubtrieType::Lower(_))
732                    {
733                        curr_subtrie_type = next_subtrie_type;
734                    }
735                }
736            };
737        }
738
739        // Before mutating, check if branch collapse would require revealing a blinded node.
740        // This ensures remove_leaf is atomic: if it errors, the trie is unchanged.
741        if let (Some(branch_path), Some(SparseNode::Branch { state_mask, .. })) =
742            (&branch_parent_path, &branch_parent_node)
743        {
744            let mut check_mask = *state_mask;
745            let child_nibble = leaf_path.get_unchecked(branch_path.len());
746            check_mask.unset_bit(child_nibble);
747
748            if check_mask.count_bits() == 1 {
749                // Branch will collapse - check if remaining child needs revealing
750                let remaining_child_path = {
751                    let mut p = *branch_path;
752                    p.push_unchecked(
753                        check_mask.first_set_bit_index().expect("state mask is not empty"),
754                    );
755                    p
756                };
757
758                // Pre-validate the entire reveal chain (including extension grandchildren).
759                // This check mirrors the logic in `reveal_remaining_child_on_leaf_removal` with
760                // `recurse_into_extension: true` to ensure all nodes that would be revealed
761                // are accessible before any mutations occur.
762                self.pre_validate_reveal_chain(&remaining_child_path, &provider)?;
763            }
764        }
765
766        // We've traversed to the leaf and collected its ancestors as necessary. Remove the leaf
767        // from its SparseSubtrie and reset the hashes of the nodes along the path.
768        self.prefix_set.insert(*full_path);
769        let leaf_subtrie = match leaf_subtrie_type {
770            SparseSubtrieType::Upper => &mut self.upper_subtrie,
771            SparseSubtrieType::Lower(idx) => {
772                self.lower_subtries[idx].as_revealed_mut().expect("lower subtrie is revealed")
773            }
774        };
775        leaf_subtrie.inner.values.remove(full_path);
776        for (subtrie_type, path) in paths_to_reset_hashes {
777            let node = match subtrie_type {
778                SparseSubtrieType::Upper => self.upper_subtrie.nodes.get_mut(&path),
779                SparseSubtrieType::Lower(idx) => self.lower_subtries[idx]
780                    .as_revealed_mut()
781                    .expect("lower subtrie is revealed")
782                    .nodes
783                    .get_mut(&path),
784            }
785            .expect("node exists");
786
787            match node {
788                SparseNode::Extension { hash, .. } | SparseNode::Branch { hash, .. } => {
789                    *hash = None
790                }
791                SparseNode::Empty | SparseNode::Hash(_) | SparseNode::Leaf { .. } => {
792                    unreachable!("only branch and extension node hashes can be reset")
793                }
794            }
795        }
796        self.remove_node(&leaf_path);
797
798        // If the leaf was at the root replace its node with the empty value. We can stop execution
799        // here, all remaining logic is related to the ancestors of the leaf.
800        if leaf_path.is_empty() {
801            self.upper_subtrie.nodes.insert(leaf_path, SparseNode::Empty);
802            return Ok(())
803        }
804
805        // If there is a parent branch node (very likely, unless the leaf is at the root) execute
806        // any required changes for that node, relative to the removed leaf.
807        if let (Some(branch_path), &Some(SparseNode::Branch { mut state_mask, .. })) =
808            (&branch_parent_path, &branch_parent_node)
809        {
810            let child_nibble = leaf_path.get_unchecked(branch_path.len());
811            state_mask.unset_bit(child_nibble);
812
813            let new_branch_node = if state_mask.count_bits() == 1 {
814                // If only one child is left set in the branch node, we need to collapse it. Get
815                // full path of the only child node left.
816                let remaining_child_path = {
817                    let mut p = *branch_path;
818                    p.push_unchecked(
819                        state_mask.first_set_bit_index().expect("state mask is not empty"),
820                    );
821                    p
822                };
823
824                trace!(
825                    target: "trie::parallel_sparse",
826                    ?leaf_path,
827                    ?branch_path,
828                    ?remaining_child_path,
829                    "Branch node has only one child",
830                );
831
832                // If the remaining child node is not yet revealed then we have to reveal it here,
833                // otherwise it's not possible to know how to collapse the branch.
834                let remaining_child_node = self.reveal_remaining_child_on_leaf_removal(
835                    provider,
836                    full_path,
837                    &remaining_child_path,
838                    true, // recurse_into_extension
839                )?;
840
841                let (new_branch_node, remove_child) = Self::branch_changes_on_leaf_removal(
842                    branch_path,
843                    &remaining_child_path,
844                    &remaining_child_node,
845                );
846
847                if remove_child {
848                    self.move_value_on_leaf_removal(
849                        branch_path,
850                        &new_branch_node,
851                        &remaining_child_path,
852                    );
853                    self.remove_node(&remaining_child_path);
854                }
855
856                if let Some(updates) = self.updates.as_mut() {
857                    updates.updated_nodes.remove(branch_path);
858                    updates.removed_nodes.insert(*branch_path);
859                }
860
861                new_branch_node
862            } else {
863                // If more than one child is left set in the branch, we just re-insert it with the
864                // updated state_mask.
865                SparseNode::new_branch(state_mask)
866            };
867
868            let branch_subtrie = self.subtrie_for_path_mut(branch_path);
869            branch_subtrie.nodes.insert(*branch_path, new_branch_node.clone());
870            branch_parent_node = Some(new_branch_node);
871        };
872
873        // If there is a grandparent extension node then there will necessarily be a parent branch
874        // node. Execute any required changes for the extension node, relative to the (possibly now
875        // replaced with a leaf or extension) branch node.
876        if let (Some(ext_path), Some(SparseNode::Extension { key: shortkey, .. })) =
877            (ext_grandparent_path, &ext_grandparent_node)
878        {
879            let ext_subtrie = self.subtrie_for_path_mut(&ext_path);
880            let branch_path = branch_parent_path.as_ref().unwrap();
881
882            if let Some(new_ext_node) = Self::extension_changes_on_leaf_removal(
883                &ext_path,
884                shortkey,
885                branch_path,
886                branch_parent_node.as_ref().unwrap(),
887            ) {
888                ext_subtrie.nodes.insert(ext_path, new_ext_node.clone());
889                self.move_value_on_leaf_removal(&ext_path, &new_ext_node, branch_path);
890                self.remove_node(branch_path);
891            }
892        }
893
894        Ok(())
895    }
896
897    #[instrument(level = "trace", target = "trie::sparse::parallel", skip(self))]
898    fn root(&mut self) -> B256 {
899        trace!(target: "trie::parallel_sparse", "Calculating trie root hash");
900
901        if self.prefix_set.is_empty() &&
902            let Some(hash) =
903                self.upper_subtrie.nodes.get(&Nibbles::default()).and_then(|node| node.hash())
904        {
905            return hash;
906        }
907
908        // Update all lower subtrie hashes
909        self.update_subtrie_hashes();
910
911        // Update hashes for the upper subtrie using our specialized function
912        // that can access both upper and lower subtrie nodes
913        let mut prefix_set = core::mem::take(&mut self.prefix_set).freeze();
914        let root_rlp = self.update_upper_subtrie_hashes(&mut prefix_set);
915
916        // Return the root hash
917        root_rlp.as_hash().unwrap_or(EMPTY_ROOT_HASH)
918    }
919
920    fn is_root_cached(&self) -> bool {
921        self.prefix_set.is_empty() &&
922            self.upper_subtrie
923                .nodes
924                .get(&Nibbles::default())
925                .is_some_and(|node| node.hash().is_some())
926    }
927
928    #[instrument(level = "trace", target = "trie::sparse::parallel", skip(self))]
929    fn update_subtrie_hashes(&mut self) {
930        trace!(target: "trie::parallel_sparse", "Updating subtrie hashes");
931
932        // Take changed subtries according to the prefix set
933        let mut prefix_set = core::mem::take(&mut self.prefix_set).freeze();
934        let num_changed_keys = prefix_set.len();
935        let (mut changed_subtries, unchanged_prefix_set) =
936            self.take_changed_lower_subtries(&mut prefix_set);
937
938        // update metrics
939        #[cfg(feature = "metrics")]
940        self.metrics.subtries_updated.record(changed_subtries.len() as f64);
941
942        // Update the prefix set with the keys that didn't have matching subtries
943        self.prefix_set = unchanged_prefix_set;
944
945        // Update subtrie hashes serially parallelism is not enabled
946        if !self.is_update_parallelism_enabled(num_changed_keys) {
947            for changed_subtrie in &mut changed_subtries {
948                changed_subtrie.subtrie.update_hashes(
949                    &mut changed_subtrie.prefix_set,
950                    &mut changed_subtrie.update_actions_buf,
951                    &self.branch_node_masks,
952                );
953            }
954
955            self.insert_changed_subtries(changed_subtries);
956            return
957        }
958
959        #[cfg(not(feature = "std"))]
960        unreachable!("nostd is checked by is_update_parallelism_enabled");
961
962        #[cfg(feature = "std")]
963        // Update subtrie hashes in parallel
964        {
965            use rayon::prelude::*;
966
967            changed_subtries.par_iter_mut().for_each(|changed_subtrie| {
968                #[cfg(feature = "metrics")]
969                let start = std::time::Instant::now();
970                changed_subtrie.subtrie.update_hashes(
971                    &mut changed_subtrie.prefix_set,
972                    &mut changed_subtrie.update_actions_buf,
973                    &self.branch_node_masks,
974                );
975                #[cfg(feature = "metrics")]
976                self.metrics.subtrie_hash_update_latency.record(start.elapsed());
977            });
978
979            self.insert_changed_subtries(changed_subtries);
980        }
981    }
982
983    fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
984        // `subtrie_for_path` is intended for a node path, but here we are using a full key path. So
985        // we need to check if the subtrie that the key might belong to has any nodes; if not then
986        // the key's portion of the trie doesn't have enough depth to reach into the subtrie, and
987        // the key will be in the upper subtrie
988        if let Some(subtrie) = self.subtrie_for_path(full_path) &&
989            !subtrie.is_empty()
990        {
991            return subtrie.inner.values.get(full_path);
992        }
993
994        self.upper_subtrie.inner.values.get(full_path)
995    }
996
997    fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
998        self.updates.as_ref().map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
999    }
1000
1001    fn take_updates(&mut self) -> SparseTrieUpdates {
1002        match self.updates.take() {
1003            Some(updates) => {
1004                // Sync branch_node_masks with what's being committed to DB.
1005                // This ensures that on subsequent root() calls, the masks reflect the actual
1006                // DB state, which is needed for correct removal detection.
1007                for (path, node) in &updates.updated_nodes {
1008                    self.branch_node_masks.insert(
1009                        *path,
1010                        BranchNodeMasks { tree_mask: node.tree_mask, hash_mask: node.hash_mask },
1011                    );
1012                }
1013                for path in &updates.removed_nodes {
1014                    self.branch_node_masks.remove(path);
1015                }
1016
1017                // NOTE: we need to preserve Some case
1018                self.updates = Some(SparseTrieUpdates::with_capacity(
1019                    updates.updated_nodes.len(),
1020                    updates.removed_nodes.len(),
1021                ));
1022                updates
1023            }
1024            None => SparseTrieUpdates::default(),
1025        }
1026    }
1027
1028    fn wipe(&mut self) {
1029        self.upper_subtrie.wipe();
1030        for trie in &mut *self.lower_subtries {
1031            trie.wipe();
1032        }
1033        self.prefix_set = PrefixSetMut::all();
1034        self.updates = self.updates.is_some().then(SparseTrieUpdates::wiped);
1035        self.subtrie_heat.clear();
1036    }
1037
1038    fn clear(&mut self) {
1039        self.upper_subtrie.clear();
1040        self.upper_subtrie.nodes.insert(Nibbles::default(), SparseNode::Empty);
1041        for subtrie in &mut *self.lower_subtries {
1042            subtrie.clear();
1043        }
1044        self.prefix_set.clear();
1045        self.updates = None;
1046        self.branch_node_masks.clear();
1047        self.subtrie_heat.clear();
1048        // `update_actions_buffers` doesn't need to be cleared; we want to reuse the Vecs it has
1049        // buffered, and all of those are already inherently cleared when they get used.
1050    }
1051
1052    fn find_leaf(
1053        &self,
1054        full_path: &Nibbles,
1055        expected_value: Option<&Vec<u8>>,
1056    ) -> Result<LeafLookup, LeafLookupError> {
1057        // Inclusion proof
1058        //
1059        // First, do a quick check if the value exists in either the upper or lower subtrie's values
1060        // map. We assume that if there exists a leaf node, then its value will be in the `values`
1061        // map.
1062        if let Some(actual_value) = core::iter::once(self.upper_subtrie.as_ref())
1063            .chain(self.lower_subtrie_for_path(full_path))
1064            .filter_map(|subtrie| subtrie.inner.values.get(full_path))
1065            .next()
1066        {
1067            // We found the leaf, check if the value matches (if expected value was provided)
1068            return expected_value
1069                .is_none_or(|v| v == actual_value)
1070                .then_some(LeafLookup::Exists)
1071                .ok_or_else(|| LeafLookupError::ValueMismatch {
1072                    path: *full_path,
1073                    expected: expected_value.cloned(),
1074                    actual: actual_value.clone(),
1075                })
1076        }
1077
1078        // If the value does not exist in the `values` map, then this means that the leaf either:
1079        // - Does not exist in the trie
1080        // - Is missing from the witness
1081        // We traverse the trie to find the location where this leaf would have been, showing
1082        // that it is not in the trie. Or we find a blinded node, showing that the witness is
1083        // not complete.
1084        let mut curr_path = Nibbles::new(); // start traversal from root
1085        let mut curr_subtrie = self.upper_subtrie.as_ref();
1086        let mut curr_subtrie_is_upper = true;
1087
1088        loop {
1089            let curr_node = curr_subtrie.nodes.get(&curr_path).unwrap();
1090
1091            match Self::find_next_to_leaf(&curr_path, curr_node, full_path) {
1092                FindNextToLeafOutcome::NotFound => return Ok(LeafLookup::NonExistent),
1093                FindNextToLeafOutcome::BlindedNode(hash) => {
1094                    // We hit a blinded node - cannot determine if leaf exists
1095                    return Err(LeafLookupError::BlindedNode { path: curr_path, hash });
1096                }
1097                FindNextToLeafOutcome::Found => {
1098                    panic!("target leaf {full_path:?} found at path {curr_path:?}, even though value wasn't in values hashmap");
1099                }
1100                FindNextToLeafOutcome::ContinueFrom(next_path) => {
1101                    curr_path = next_path;
1102                    // If we were previously looking at the upper trie, and the new path is in the
1103                    // lower trie, we need to pull out a ref to the lower trie.
1104                    if curr_subtrie_is_upper &&
1105                        let Some(lower_subtrie) = self.lower_subtrie_for_path(&curr_path)
1106                    {
1107                        curr_subtrie = lower_subtrie;
1108                        curr_subtrie_is_upper = false;
1109                    }
1110                }
1111            }
1112        }
1113    }
1114
1115    fn shrink_nodes_to(&mut self, size: usize) {
1116        // Distribute the capacity across upper and lower subtries
1117        //
1118        // Always include upper subtrie, plus any lower subtries
1119        let total_subtries = 1 + NUM_LOWER_SUBTRIES;
1120        let size_per_subtrie = size / total_subtries;
1121
1122        // Shrink the upper subtrie
1123        self.upper_subtrie.shrink_nodes_to(size_per_subtrie);
1124
1125        // Shrink lower subtries (works for both revealed and blind with allocation)
1126        for subtrie in &mut *self.lower_subtries {
1127            subtrie.shrink_nodes_to(size_per_subtrie);
1128        }
1129
1130        // shrink masks map
1131        self.branch_node_masks.shrink_to(size);
1132    }
1133
1134    fn shrink_values_to(&mut self, size: usize) {
1135        // Distribute the capacity across upper and lower subtries
1136        //
1137        // Always include upper subtrie, plus any lower subtries
1138        let total_subtries = 1 + NUM_LOWER_SUBTRIES;
1139        let size_per_subtrie = size / total_subtries;
1140
1141        // Shrink the upper subtrie
1142        self.upper_subtrie.shrink_values_to(size_per_subtrie);
1143
1144        // Shrink lower subtries (works for both revealed and blind with allocation)
1145        for subtrie in &mut *self.lower_subtries {
1146            subtrie.shrink_values_to(size_per_subtrie);
1147        }
1148    }
1149
1150    /// O(1) size hint based on total node count (including hash stubs).
1151    fn size_hint(&self) -> usize {
1152        let upper_count = self.upper_subtrie.nodes.len();
1153        let lower_count: usize = self
1154            .lower_subtries
1155            .iter()
1156            .filter_map(|s| s.as_revealed_ref())
1157            .map(|s| s.nodes.len())
1158            .sum();
1159        upper_count + lower_count
1160    }
1161
1162    fn prune(&mut self, max_depth: usize) -> usize {
1163        // Decay heat for subtries not modified this cycle
1164        self.subtrie_heat.decay_and_reset();
1165
1166        // DFS traversal to find nodes at max_depth that can be pruned.
1167        // Collects "effective pruned roots" - children of nodes at max_depth with computed hashes.
1168        // We replace nodes with Hash stubs inline during traversal.
1169        let mut effective_pruned_roots = Vec::<(Nibbles, B256)>::new();
1170        let mut stack: SmallVec<[(Nibbles, usize); 32]> = SmallVec::new();
1171        stack.push((Nibbles::default(), 0));
1172
1173        // DFS traversal: pop path and depth, skip if subtrie or node not found.
1174        while let Some((path, depth)) = stack.pop() {
1175            // Skip traversal into hot lower subtries beyond max_depth.
1176            // At max_depth, we still need to process the node to convert children to hashes.
1177            // This keeps frequently-modified subtries revealed to avoid expensive re-reveals.
1178            if depth > max_depth &&
1179                let SparseSubtrieType::Lower(idx) = SparseSubtrieType::from_path(&path) &&
1180                self.subtrie_heat.is_hot(idx)
1181            {
1182                continue;
1183            }
1184
1185            // Get children to visit from current node (immutable access)
1186            let children: SmallVec<[Nibbles; 16]> = {
1187                let Some(subtrie) = self.subtrie_for_path(&path) else { continue };
1188                let Some(node) = subtrie.nodes.get(&path) else { continue };
1189
1190                match node {
1191                    SparseNode::Empty | SparseNode::Hash(_) | SparseNode::Leaf { .. } => {
1192                        SmallVec::new()
1193                    }
1194                    SparseNode::Extension { key, .. } => {
1195                        let mut child = path;
1196                        child.extend(key);
1197                        SmallVec::from_slice(&[child])
1198                    }
1199                    SparseNode::Branch { state_mask, .. } => {
1200                        let mut children = SmallVec::new();
1201                        let mut mask = state_mask.get();
1202                        while mask != 0 {
1203                            let nibble = mask.trailing_zeros() as u8;
1204                            mask &= mask - 1;
1205                            let mut child = path;
1206                            child.push_unchecked(nibble);
1207                            children.push(child);
1208                        }
1209                        children
1210                    }
1211                }
1212            };
1213
1214            // Process children - either continue traversal or prune
1215            for child in children {
1216                if depth == max_depth {
1217                    // Check if child has a computed hash and replace inline
1218                    let hash = self
1219                        .subtrie_for_path(&child)
1220                        .and_then(|s| s.nodes.get(&child))
1221                        .filter(|n| !n.is_hash())
1222                        .and_then(|n| n.hash());
1223
1224                    if let Some(hash) = hash {
1225                        // Use untracked access to avoid marking subtrie as modified during pruning
1226                        if let Some(subtrie) = self.subtrie_for_path_mut_untracked(&child) {
1227                            subtrie.nodes.insert(child, SparseNode::Hash(hash));
1228                            effective_pruned_roots.push((child, hash));
1229                        }
1230                    }
1231                } else {
1232                    stack.push((child, depth + 1));
1233                }
1234            }
1235        }
1236
1237        if effective_pruned_roots.is_empty() {
1238            return 0;
1239        }
1240
1241        let nodes_converted = effective_pruned_roots.len();
1242
1243        // Sort roots by subtrie type (upper first), then by path for efficient partitioning.
1244        effective_pruned_roots.sort_unstable_by(|(path_a, _), (path_b, _)| {
1245            let subtrie_type_a = SparseSubtrieType::from_path(path_a);
1246            let subtrie_type_b = SparseSubtrieType::from_path(path_b);
1247            subtrie_type_a.cmp(&subtrie_type_b).then(path_a.cmp(path_b))
1248        });
1249
1250        // Split off upper subtrie roots (they come first due to sorting)
1251        let num_upper_roots = effective_pruned_roots
1252            .iter()
1253            .position(|(p, _)| !SparseSubtrieType::path_len_is_upper(p.len()))
1254            .unwrap_or(effective_pruned_roots.len());
1255
1256        let roots_upper = &effective_pruned_roots[..num_upper_roots];
1257        let roots_lower = &effective_pruned_roots[num_upper_roots..];
1258
1259        debug_assert!(
1260            {
1261                let mut all_roots: Vec<_> = effective_pruned_roots.iter().map(|(p, _)| p).collect();
1262                all_roots.sort_unstable();
1263                all_roots.windows(2).all(|w| !w[1].starts_with(w[0]))
1264            },
1265            "prune roots must be prefix-free"
1266        );
1267
1268        // Upper prune roots that are prefixes of lower subtrie root paths cause the entire
1269        // subtrie to be cleared (preserving allocations for reuse).
1270        if !roots_upper.is_empty() {
1271            for subtrie in &mut *self.lower_subtries {
1272                let should_clear = subtrie.as_revealed_ref().is_some_and(|s| {
1273                    let search_idx = roots_upper.partition_point(|(root, _)| root <= &s.path);
1274                    search_idx > 0 && s.path.starts_with(&roots_upper[search_idx - 1].0)
1275                });
1276                if should_clear {
1277                    subtrie.clear();
1278                }
1279            }
1280        }
1281
1282        // Upper subtrie: prune nodes and values
1283        self.upper_subtrie.nodes.retain(|p, _| !is_strict_descendant_in(roots_upper, p));
1284        self.upper_subtrie.inner.values.retain(|p, _| {
1285            !starts_with_pruned_in(roots_upper, p) && !starts_with_pruned_in(roots_lower, p)
1286        });
1287
1288        // Process lower subtries using chunk_by to group roots by subtrie
1289        for roots_group in roots_lower.chunk_by(|(path_a, _), (path_b, _)| {
1290            SparseSubtrieType::from_path(path_a) == SparseSubtrieType::from_path(path_b)
1291        }) {
1292            let subtrie_idx = path_subtrie_index_unchecked(&roots_group[0].0);
1293
1294            // Skip unrevealed/blinded subtries - nothing to prune
1295            let Some(subtrie) = self.lower_subtries[subtrie_idx].as_revealed_mut() else {
1296                continue;
1297            };
1298
1299            // Retain only nodes/values not descended from any pruned root.
1300            subtrie.nodes.retain(|p, _| !is_strict_descendant_in(roots_group, p));
1301            subtrie.inner.values.retain(|p, _| !starts_with_pruned_in(roots_group, p));
1302        }
1303
1304        // Branch node masks pruning
1305        self.branch_node_masks.retain(|p, _| {
1306            if SparseSubtrieType::path_len_is_upper(p.len()) {
1307                !starts_with_pruned_in(roots_upper, p)
1308            } else {
1309                !starts_with_pruned_in(roots_lower, p) && !starts_with_pruned_in(roots_upper, p)
1310            }
1311        });
1312
1313        nodes_converted
1314    }
1315
1316    fn update_leaves(
1317        &mut self,
1318        updates: &mut alloy_primitives::map::B256Map<crate::LeafUpdate>,
1319        mut proof_required_fn: impl FnMut(B256, u8),
1320    ) -> SparseTrieResult<()> {
1321        use crate::{provider::NoRevealProvider, LeafUpdate};
1322
1323        // Collect keys upfront since we mutate `updates` during iteration.
1324        // On success, entries are removed; on blinded node failure, they're re-inserted.
1325        let keys: Vec<B256> = updates.keys().copied().collect();
1326
1327        for key in keys {
1328            let full_path = Nibbles::unpack(key);
1329            // Remove upfront - we'll re-insert if the operation fails due to blinded node.
1330            let update = updates.remove(&key).unwrap();
1331
1332            match update {
1333                LeafUpdate::Changed(value) => {
1334                    if value.is_empty() {
1335                        // Removal: remove_leaf with NoRevealProvider is atomic - returns a
1336                        // retriable error before any mutations (via pre_validate_reveal_chain).
1337                        match self.remove_leaf(&full_path, NoRevealProvider) {
1338                            Ok(()) => {}
1339                            Err(e) => {
1340                                if let Some(path) = Self::get_retriable_path(&e) {
1341                                    let (target_key, min_len) =
1342                                        Self::proof_target_for_path(key, &full_path, &path);
1343                                    proof_required_fn(target_key, min_len);
1344                                    updates.insert(key, LeafUpdate::Changed(value));
1345                                } else {
1346                                    return Err(e);
1347                                }
1348                            }
1349                        }
1350                    } else {
1351                        // Update/insert: update_leaf is atomic - cleans up on error.
1352                        if let Err(e) = self.update_leaf(full_path, value.clone(), NoRevealProvider)
1353                        {
1354                            if let Some(path) = Self::get_retriable_path(&e) {
1355                                let (target_key, min_len) =
1356                                    Self::proof_target_for_path(key, &full_path, &path);
1357                                proof_required_fn(target_key, min_len);
1358                                updates.insert(key, LeafUpdate::Changed(value));
1359                            } else {
1360                                return Err(e);
1361                            }
1362                        }
1363                    }
1364                }
1365                LeafUpdate::Touched => {
1366                    // Touched is read-only: check if path is accessible, request proof if blinded.
1367                    match self.find_leaf(&full_path, None) {
1368                        Err(LeafLookupError::BlindedNode { path, .. }) => {
1369                            let (target_key, min_len) =
1370                                Self::proof_target_for_path(key, &full_path, &path);
1371                            proof_required_fn(target_key, min_len);
1372                            updates.insert(key, LeafUpdate::Touched);
1373                        }
1374                        // Path is fully revealed (exists or proven non-existent), no action needed.
1375                        Ok(_) | Err(LeafLookupError::ValueMismatch { .. }) => {}
1376                    }
1377                }
1378            }
1379        }
1380
1381        Ok(())
1382    }
1383}
1384
1385impl ParallelSparseTrie {
1386    /// Sets the thresholds that control when parallelism is used during operations.
1387    pub const fn with_parallelism_thresholds(mut self, thresholds: ParallelismThresholds) -> Self {
1388        self.parallelism_thresholds = thresholds;
1389        self
1390    }
1391
1392    /// Returns true if retaining updates is enabled for the overall trie.
1393    const fn updates_enabled(&self) -> bool {
1394        self.updates.is_some()
1395    }
1396
1397    /// Returns true if parallelism should be enabled for revealing the given number of nodes.
1398    /// Will always return false in nostd builds.
1399    const fn is_reveal_parallelism_enabled(&self, num_nodes: usize) -> bool {
1400        #[cfg(not(feature = "std"))]
1401        {
1402            let _ = num_nodes;
1403            return false;
1404        }
1405
1406        #[cfg(feature = "std")]
1407        {
1408            num_nodes >= self.parallelism_thresholds.min_revealed_nodes
1409        }
1410    }
1411
1412    /// Returns true if parallelism should be enabled for updating hashes with the given number
1413    /// of changed keys. Will always return false in nostd builds.
1414    const fn is_update_parallelism_enabled(&self, num_changed_keys: usize) -> bool {
1415        #[cfg(not(feature = "std"))]
1416        {
1417            let _ = num_changed_keys;
1418            return false;
1419        }
1420
1421        #[cfg(feature = "std")]
1422        {
1423            num_changed_keys >= self.parallelism_thresholds.min_updated_nodes
1424        }
1425    }
1426
1427    /// Checks if an error is retriable (`BlindedNode` or `NodeNotFoundInProvider`) and extracts
1428    /// the path if so.
1429    ///
1430    /// Both error types indicate that a node needs to be revealed before the operation can
1431    /// succeed. `BlindedNode` occurs when traversing to a Hash node, while `NodeNotFoundInProvider`
1432    /// occurs when `retain_updates` is enabled and an extension node's child needs revealing.
1433    const fn get_retriable_path(e: &SparseTrieError) -> Option<Nibbles> {
1434        match e.kind() {
1435            SparseTrieErrorKind::BlindedNode { path, .. } |
1436            SparseTrieErrorKind::NodeNotFoundInProvider { path } => Some(*path),
1437            _ => None,
1438        }
1439    }
1440
1441    /// Converts a nibbles path to a B256, right-padding with zeros to 64 nibbles.
1442    fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
1443        let mut bytes = [0u8; 32];
1444        path.pack_to(&mut bytes);
1445        B256::from(bytes)
1446    }
1447
1448    /// Computes the proof target key and `min_len` for a blinded node error.
1449    ///
1450    /// Returns `(target_key, min_len)` where:
1451    /// - `target_key` is `full_key` if `path` is a prefix of `full_path`, otherwise the padded path
1452    /// - `min_len` is always based on `path.len()`
1453    fn proof_target_for_path(full_key: B256, full_path: &Nibbles, path: &Nibbles) -> (B256, u8) {
1454        let min_len = (path.len() as u8).min(64);
1455        let target_key =
1456            if full_path.starts_with(path) { full_key } else { Self::nibbles_to_padded_b256(path) };
1457        (target_key, min_len)
1458    }
1459
1460    /// Rolls back a partial update by removing the value, removing any inserted nodes,
1461    /// removing any inserted branch masks, and restoring any modified original node.
1462    /// This ensures `update_leaf` is atomic - either it succeeds completely or leaves the trie
1463    /// unchanged.
1464    fn rollback_insert(
1465        &mut self,
1466        full_path: &Nibbles,
1467        inserted_nodes: &[Nibbles],
1468        inserted_masks: &[Nibbles],
1469        modified_original: Option<(Nibbles, SparseNode)>,
1470    ) {
1471        self.upper_subtrie.inner.values.remove(full_path);
1472        for node_path in inserted_nodes {
1473            // Try upper subtrie first - nodes may be there even if path length suggests lower
1474            if self.upper_subtrie.nodes.remove(node_path).is_none() {
1475                // Not in upper, try lower subtrie
1476                if let Some(subtrie) = self.lower_subtrie_for_path_mut(node_path) {
1477                    subtrie.nodes.remove(node_path);
1478                }
1479            }
1480        }
1481        // Remove any branch masks that were inserted
1482        for mask_path in inserted_masks {
1483            self.branch_node_masks.remove(mask_path);
1484        }
1485        // Restore the original node that was modified
1486        if let Some((path, original_node)) = modified_original {
1487            self.upper_subtrie.nodes.insert(path, original_node);
1488        }
1489    }
1490
1491    /// Creates a new revealed sparse trie from the given root node.
1492    ///
1493    /// This function initializes the internal structures and then reveals the root.
1494    /// It is a convenient method to create a trie when you already have the root node available.
1495    ///
1496    /// # Arguments
1497    ///
1498    /// * `root` - The root node of the trie
1499    /// * `masks` - Trie masks for root branch node
1500    /// * `retain_updates` - Whether to track updates
1501    ///
1502    /// # Returns
1503    ///
1504    /// Self if successful, or an error if revealing fails.
1505    pub fn from_root(
1506        root: TrieNode,
1507        masks: Option<BranchNodeMasks>,
1508        retain_updates: bool,
1509    ) -> SparseTrieResult<Self> {
1510        Self::default().with_root(root, masks, retain_updates)
1511    }
1512
1513    /// Returns a reference to the lower `SparseSubtrie` for the given path, or None if the
1514    /// path belongs to the upper trie, or if the lower subtrie for the path doesn't exist or is
1515    /// blinded.
1516    fn lower_subtrie_for_path(&self, path: &Nibbles) -> Option<&SparseSubtrie> {
1517        match SparseSubtrieType::from_path(path) {
1518            SparseSubtrieType::Upper => None,
1519            SparseSubtrieType::Lower(idx) => self.lower_subtries[idx].as_revealed_ref(),
1520        }
1521    }
1522
1523    /// Returns a mutable reference to the lower `SparseSubtrie` for the given path, or None if the
1524    /// path belongs to the upper trie.
1525    ///
1526    /// This method will create/reveal a new lower subtrie for the given path if one isn't already.
1527    /// If one does exist, but its path field is longer than the given path, then the field will be
1528    /// set to the given path.
1529    fn lower_subtrie_for_path_mut(&mut self, path: &Nibbles) -> Option<&mut SparseSubtrie> {
1530        match SparseSubtrieType::from_path(path) {
1531            SparseSubtrieType::Upper => None,
1532            SparseSubtrieType::Lower(idx) => {
1533                self.lower_subtries[idx].reveal(path);
1534                self.subtrie_heat.mark_modified(idx);
1535                Some(self.lower_subtries[idx].as_revealed_mut().expect("just revealed"))
1536            }
1537        }
1538    }
1539
1540    /// Returns a reference to either the lower or upper `SparseSubtrie` for the given path,
1541    /// depending on the path's length.
1542    ///
1543    /// Returns `None` if a lower subtrie does not exist for the given path.
1544    fn subtrie_for_path(&self, path: &Nibbles) -> Option<&SparseSubtrie> {
1545        if SparseSubtrieType::path_len_is_upper(path.len()) {
1546            Some(&self.upper_subtrie)
1547        } else {
1548            self.lower_subtrie_for_path(path)
1549        }
1550    }
1551
1552    /// Returns a mutable reference to either the lower or upper `SparseSubtrie` for the given path,
1553    /// depending on the path's length.
1554    ///
1555    /// This method will create/reveal a new lower subtrie for the given path if one isn't already.
1556    /// If one does exist, but its path field is longer than the given path, then the field will be
1557    /// set to the given path.
1558    fn subtrie_for_path_mut(&mut self, path: &Nibbles) -> &mut SparseSubtrie {
1559        // We can't just call `lower_subtrie_for_path` and return `upper_subtrie` if it returns
1560        // None, because Rust complains about double mutable borrowing `self`.
1561        if SparseSubtrieType::path_len_is_upper(path.len()) {
1562            &mut self.upper_subtrie
1563        } else {
1564            self.lower_subtrie_for_path_mut(path).unwrap()
1565        }
1566    }
1567
1568    /// Returns a mutable reference to a subtrie without marking it as modified.
1569    /// Used for internal operations like pruning that shouldn't affect heat tracking.
1570    fn subtrie_for_path_mut_untracked(&mut self, path: &Nibbles) -> Option<&mut SparseSubtrie> {
1571        if SparseSubtrieType::path_len_is_upper(path.len()) {
1572            Some(&mut self.upper_subtrie)
1573        } else {
1574            match SparseSubtrieType::from_path(path) {
1575                SparseSubtrieType::Upper => None,
1576                SparseSubtrieType::Lower(idx) => self.lower_subtries[idx].as_revealed_mut(),
1577            }
1578        }
1579    }
1580
1581    /// Returns the next node in the traversal path from the given path towards the leaf for the
1582    /// given full leaf path, or an error if any node along the traversal path is not revealed.
1583    ///
1584    ///
1585    /// ## Panics
1586    ///
1587    /// If `from_path` is not a prefix of `leaf_full_path`.
1588    fn find_next_to_leaf(
1589        from_path: &Nibbles,
1590        from_node: &SparseNode,
1591        leaf_full_path: &Nibbles,
1592    ) -> FindNextToLeafOutcome {
1593        debug_assert!(leaf_full_path.len() >= from_path.len());
1594        debug_assert!(leaf_full_path.starts_with(from_path));
1595
1596        match from_node {
1597            // If empty node is found it means the subtrie doesn't have any nodes in it, let alone
1598            // the target leaf.
1599            SparseNode::Empty => FindNextToLeafOutcome::NotFound,
1600            SparseNode::Hash(hash) => FindNextToLeafOutcome::BlindedNode(*hash),
1601            SparseNode::Leaf { key, .. } => {
1602                let mut found_full_path = *from_path;
1603                found_full_path.extend(key);
1604
1605                if &found_full_path == leaf_full_path {
1606                    return FindNextToLeafOutcome::Found
1607                }
1608                FindNextToLeafOutcome::NotFound
1609            }
1610            SparseNode::Extension { key, .. } => {
1611                if leaf_full_path.len() == from_path.len() {
1612                    return FindNextToLeafOutcome::NotFound
1613                }
1614
1615                let mut child_path = *from_path;
1616                child_path.extend(key);
1617
1618                if !leaf_full_path.starts_with(&child_path) {
1619                    return FindNextToLeafOutcome::NotFound
1620                }
1621                FindNextToLeafOutcome::ContinueFrom(child_path)
1622            }
1623            SparseNode::Branch { state_mask, .. } => {
1624                if leaf_full_path.len() == from_path.len() {
1625                    return FindNextToLeafOutcome::NotFound
1626                }
1627
1628                let nibble = leaf_full_path.get_unchecked(from_path.len());
1629                if !state_mask.is_bit_set(nibble) {
1630                    return FindNextToLeafOutcome::NotFound
1631                }
1632
1633                let mut child_path = *from_path;
1634                child_path.push_unchecked(nibble);
1635
1636                FindNextToLeafOutcome::ContinueFrom(child_path)
1637            }
1638        }
1639    }
1640
1641    /// Called when a child node has collapsed into its parent as part of `remove_leaf`. If the
1642    /// new parent node is a leaf, then the previous child also was, and if the previous child was
1643    /// on a lower subtrie while the parent is on an upper then the leaf value needs to be moved to
1644    /// the upper.
1645    fn move_value_on_leaf_removal(
1646        &mut self,
1647        parent_path: &Nibbles,
1648        new_parent_node: &SparseNode,
1649        prev_child_path: &Nibbles,
1650    ) {
1651        // If the parent path isn't in the upper then it doesn't matter what the new node is,
1652        // there's no situation where a leaf value needs to be moved.
1653        if SparseSubtrieType::from_path(parent_path).lower_index().is_some() {
1654            return;
1655        }
1656
1657        if let SparseNode::Leaf { key, .. } = new_parent_node {
1658            let Some(prev_child_subtrie) = self.lower_subtrie_for_path_mut(prev_child_path) else {
1659                return;
1660            };
1661
1662            let mut leaf_full_path = *parent_path;
1663            leaf_full_path.extend(key);
1664
1665            let val = prev_child_subtrie.inner.values.remove(&leaf_full_path).expect("ParallelSparseTrie is in an inconsistent state, expected value on subtrie which wasn't found");
1666            self.upper_subtrie.inner.values.insert(leaf_full_path, val);
1667        }
1668    }
1669
1670    /// Used by `remove_leaf` to ensure that when a node is removed from a lower subtrie that any
1671    /// externalities are handled. These can include:
1672    /// - Removing the lower subtrie completely, if it is now empty.
1673    /// - Updating the `path` field of the lower subtrie to indicate that its root node has changed.
1674    ///
1675    /// This method assumes that the caller will deal with putting all other nodes in the trie into
1676    /// a consistent state after the removal of this one.
1677    ///
1678    /// ## Panics
1679    ///
1680    /// - If the removed node was not a leaf or extension.
1681    fn remove_node(&mut self, path: &Nibbles) {
1682        let subtrie = self.subtrie_for_path_mut(path);
1683        let node = subtrie.nodes.remove(path);
1684
1685        let Some(idx) = SparseSubtrieType::from_path(path).lower_index() else {
1686            // When removing a node from the upper trie there's nothing special we need to do to fix
1687            // its path field; the upper trie's path is always empty.
1688            return;
1689        };
1690
1691        match node {
1692            Some(SparseNode::Leaf { .. }) => {
1693                // If the leaf was the final node in its lower subtrie then we can blind the
1694                // subtrie, effectively marking it as empty.
1695                if subtrie.nodes.is_empty() {
1696                    self.lower_subtries[idx].clear();
1697                }
1698            }
1699            Some(SparseNode::Extension { key, .. }) => {
1700                // If the removed extension was the root node of a lower subtrie then the lower
1701                // subtrie's `path` needs to be updated to be whatever node the extension used to
1702                // point to.
1703                if &subtrie.path == path {
1704                    subtrie.path.extend(&key);
1705                }
1706            }
1707            _ => panic!("Expected to remove a leaf or extension, but removed {node:?}"),
1708        }
1709    }
1710
1711    /// Given the path to a parent branch node and a child node which is the sole remaining child on
1712    /// that branch after removing a leaf, returns a node to replace the parent branch node and a
1713    /// boolean indicating if the child should be deleted.
1714    ///
1715    /// ## Panics
1716    ///
1717    /// - If either parent or child node is not already revealed.
1718    /// - If parent's path is not a prefix of the child's path.
1719    fn branch_changes_on_leaf_removal(
1720        parent_path: &Nibbles,
1721        remaining_child_path: &Nibbles,
1722        remaining_child_node: &SparseNode,
1723    ) -> (SparseNode, bool) {
1724        debug_assert!(remaining_child_path.len() > parent_path.len());
1725        debug_assert!(remaining_child_path.starts_with(parent_path));
1726
1727        let remaining_child_nibble = remaining_child_path.get_unchecked(parent_path.len());
1728
1729        // If we swap the branch node out either an extension or leaf, depending on
1730        // what its remaining child is.
1731        match remaining_child_node {
1732            SparseNode::Empty | SparseNode::Hash(_) => {
1733                panic!("remaining child must have been revealed already")
1734            }
1735            // If the only child is a leaf node, we downgrade the branch node into a
1736            // leaf node, prepending the nibble to the key, and delete the old
1737            // child.
1738            SparseNode::Leaf { key, .. } => {
1739                let mut new_key = Nibbles::from_nibbles_unchecked([remaining_child_nibble]);
1740                new_key.extend(key);
1741                (SparseNode::new_leaf(new_key), true)
1742            }
1743            // If the only child node is an extension node, we downgrade the branch
1744            // node into an even longer extension node, prepending the nibble to the
1745            // key, and delete the old child.
1746            SparseNode::Extension { key, .. } => {
1747                let mut new_key = Nibbles::from_nibbles_unchecked([remaining_child_nibble]);
1748                new_key.extend(key);
1749                (SparseNode::new_ext(new_key), true)
1750            }
1751            // If the only child is a branch node, we downgrade the current branch
1752            // node into a one-nibble extension node.
1753            SparseNode::Branch { .. } => (
1754                SparseNode::new_ext(Nibbles::from_nibbles_unchecked([remaining_child_nibble])),
1755                false,
1756            ),
1757        }
1758    }
1759
1760    /// Given the path to a parent extension and its key, and a child node (not necessarily on this
1761    /// subtrie), returns an optional replacement parent node. If a replacement is returned then the
1762    /// child node should be deleted.
1763    ///
1764    /// ## Panics
1765    ///
1766    /// - If either parent or child node is not already revealed.
1767    /// - If parent's path is not a prefix of the child's path.
1768    fn extension_changes_on_leaf_removal(
1769        parent_path: &Nibbles,
1770        parent_key: &Nibbles,
1771        child_path: &Nibbles,
1772        child: &SparseNode,
1773    ) -> Option<SparseNode> {
1774        debug_assert!(child_path.len() > parent_path.len());
1775        debug_assert!(child_path.starts_with(parent_path));
1776
1777        // If the parent node is an extension node, we need to look at its child to see
1778        // if we need to merge it.
1779        match child {
1780            SparseNode::Empty | SparseNode::Hash(_) => {
1781                panic!("child must be revealed")
1782            }
1783            // For a leaf node, we collapse the extension node into a leaf node,
1784            // extending the key. While it's impossible to encounter an extension node
1785            // followed by a leaf node in a complete trie, it's possible here because we
1786            // could have downgraded the extension node's child into a leaf node from a
1787            // branch in a previous call to `branch_changes_on_leaf_removal`.
1788            SparseNode::Leaf { key, .. } => {
1789                let mut new_key = *parent_key;
1790                new_key.extend(key);
1791                Some(SparseNode::new_leaf(new_key))
1792            }
1793            // Similar to the leaf node, for an extension node, we collapse them into one
1794            // extension node, extending the key.
1795            SparseNode::Extension { key, .. } => {
1796                let mut new_key = *parent_key;
1797                new_key.extend(key);
1798                Some(SparseNode::new_ext(new_key))
1799            }
1800            // For a branch node, we just leave the extension node as-is.
1801            SparseNode::Branch { .. } => None,
1802        }
1803    }
1804
1805    /// Pre-validates reveal chain accessibility before mutations.
1806    ///
1807    /// Walks the trie path checking that all nodes can be revealed. This is called before
1808    /// any mutations to ensure the operation will succeed atomically.
1809    ///
1810    /// Returns `BlindedNode` error if any node in the chain cannot be revealed by the provider.
1811    fn pre_validate_reveal_chain<P: TrieNodeProvider>(
1812        &self,
1813        path: &Nibbles,
1814        provider: &P,
1815    ) -> SparseTrieResult<()> {
1816        // Find the subtrie containing this path, or return Ok if path doesn't exist
1817        let subtrie = match self.subtrie_for_path(path) {
1818            Some(s) => s,
1819            None => return Ok(()),
1820        };
1821
1822        match subtrie.nodes.get(path) {
1823            // Hash node: attempt to reveal from provider
1824            Some(SparseNode::Hash(hash)) => match provider.trie_node(path)? {
1825                Some(RevealedNode { node, .. }) => {
1826                    let decoded = TrieNode::decode(&mut &node[..])?;
1827                    // Extension nodes have children that also need validation
1828                    if let TrieNode::Extension(ext) = decoded {
1829                        let mut grandchild_path = *path;
1830                        grandchild_path.extend(&ext.key);
1831                        return self.pre_validate_reveal_chain(&grandchild_path, provider);
1832                    }
1833                    Ok(())
1834                }
1835                // Provider cannot reveal this node - operation would fail
1836                None => Err(SparseTrieErrorKind::BlindedNode { path: *path, hash: *hash }.into()),
1837            },
1838            // Already-revealed extension: recursively validate its child
1839            Some(SparseNode::Extension { key, .. }) => {
1840                let mut child_path = *path;
1841                child_path.extend(key);
1842                self.pre_validate_reveal_chain(&child_path, provider)
1843            }
1844            // Leaf, Branch, Empty, or missing: no further validation needed
1845            _ => Ok(()),
1846        }
1847    }
1848
1849    /// Called when a leaf is removed on a branch which has only one other remaining child. That
1850    /// child must be revealed in order to properly collapse the branch.
1851    ///
1852    /// If `recurse_into_extension` is true, and the remaining child is an extension node, then its
1853    /// child will be ensured to be revealed as well.
1854    ///
1855    /// ## Returns
1856    ///
1857    /// The node of the remaining child, whether it was already revealed or not.
1858    fn reveal_remaining_child_on_leaf_removal<P: TrieNodeProvider>(
1859        &mut self,
1860        provider: P,
1861        full_path: &Nibbles, // only needed for logs
1862        remaining_child_path: &Nibbles,
1863        recurse_into_extension: bool,
1864    ) -> SparseTrieResult<SparseNode> {
1865        let remaining_child_subtrie = self.subtrie_for_path_mut(remaining_child_path);
1866
1867        let (remaining_child_node, remaining_child_masks) = match remaining_child_subtrie
1868            .nodes
1869            .get(remaining_child_path)
1870            .unwrap()
1871        {
1872            SparseNode::Hash(_) => {
1873                debug!(
1874                    target: "trie::parallel_sparse",
1875                    child_path = ?remaining_child_path,
1876                    leaf_full_path = ?full_path,
1877                    "Node child not revealed in remove_leaf, falling back to db",
1878                );
1879                if let Some(RevealedNode { node, tree_mask, hash_mask }) =
1880                    provider.trie_node(remaining_child_path)?
1881                {
1882                    let decoded = TrieNode::decode(&mut &node[..])?;
1883                    trace!(
1884                        target: "trie::parallel_sparse",
1885                        ?remaining_child_path,
1886                        ?decoded,
1887                        ?tree_mask,
1888                        ?hash_mask,
1889                        "Revealing remaining blinded branch child"
1890                    );
1891                    let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
1892                    remaining_child_subtrie.reveal_node(*remaining_child_path, &decoded, masks)?;
1893                    (
1894                        remaining_child_subtrie.nodes.get(remaining_child_path).unwrap().clone(),
1895                        masks,
1896                    )
1897                } else {
1898                    return Err(SparseTrieErrorKind::NodeNotFoundInProvider {
1899                        path: *remaining_child_path,
1900                    }
1901                    .into())
1902                }
1903            }
1904            // The node is already revealed so we don't need to return its masks here, as they don't
1905            // need to be inserted.
1906            node => (node.clone(), None),
1907        };
1908
1909        if let Some(masks) = remaining_child_masks {
1910            self.branch_node_masks.insert(*remaining_child_path, masks);
1911        }
1912
1913        // If `recurse_into_extension` is true, and the remaining child is an extension node, then
1914        // its child will be ensured to be revealed as well. This is required for generation of
1915        // trie updates; without revealing the grandchild branch it's not always possible to know
1916        // if the tree mask bit should be set for the child extension on its parent branch.
1917        if let SparseNode::Extension { key, .. } = &remaining_child_node &&
1918            recurse_into_extension
1919        {
1920            let mut remaining_grandchild_path = *remaining_child_path;
1921            remaining_grandchild_path.extend(key);
1922
1923            trace!(
1924                target: "trie::parallel_sparse",
1925                remaining_grandchild_path = ?remaining_grandchild_path,
1926                child_path = ?remaining_child_path,
1927                leaf_full_path = ?full_path,
1928                "Revealing child of extension node, which is the last remaining child of the branch"
1929            );
1930
1931            self.reveal_remaining_child_on_leaf_removal(
1932                provider,
1933                full_path,
1934                &remaining_grandchild_path,
1935                false, // recurse_into_extension
1936            )?;
1937        }
1938
1939        Ok(remaining_child_node)
1940    }
1941
1942    /// Drains any [`SparseTrieUpdatesAction`]s from the given subtrie, and applies each action to
1943    /// the given `updates` set. If the given set is None then this is a no-op.
1944    #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all)]
1945    fn apply_subtrie_update_actions(
1946        &mut self,
1947        update_actions: impl Iterator<Item = SparseTrieUpdatesAction>,
1948    ) {
1949        if let Some(updates) = self.updates.as_mut() {
1950            for action in update_actions {
1951                match action {
1952                    SparseTrieUpdatesAction::InsertRemoved(path) => {
1953                        updates.updated_nodes.remove(&path);
1954                        updates.removed_nodes.insert(path);
1955                    }
1956                    SparseTrieUpdatesAction::RemoveUpdated(path) => {
1957                        updates.updated_nodes.remove(&path);
1958                    }
1959                    SparseTrieUpdatesAction::InsertUpdated(path, branch_node) => {
1960                        updates.updated_nodes.insert(path, branch_node);
1961                    }
1962                }
1963            }
1964        };
1965    }
1966
1967    /// Updates hashes for the upper subtrie, using nodes from both upper and lower subtries.
1968    #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all, ret)]
1969    fn update_upper_subtrie_hashes(&mut self, prefix_set: &mut PrefixSet) -> RlpNode {
1970        trace!(target: "trie::parallel_sparse", "Updating upper subtrie hashes");
1971
1972        debug_assert!(self.upper_subtrie.inner.buffers.path_stack.is_empty());
1973        self.upper_subtrie.inner.buffers.path_stack.push(RlpNodePathStackItem {
1974            path: Nibbles::default(), // Start from root
1975            is_in_prefix_set: None,
1976        });
1977
1978        #[cfg(feature = "metrics")]
1979        let start = std::time::Instant::now();
1980
1981        let mut update_actions_buf =
1982            self.updates_enabled().then(|| self.update_actions_buffers.pop().unwrap_or_default());
1983
1984        while let Some(stack_item) = self.upper_subtrie.inner.buffers.path_stack.pop() {
1985            let path = stack_item.path;
1986            let node = if path.len() < UPPER_TRIE_MAX_DEPTH {
1987                self.upper_subtrie.nodes.get_mut(&path).expect("upper subtrie node must exist")
1988            } else {
1989                let index = path_subtrie_index_unchecked(&path);
1990                let node = self.lower_subtries[index]
1991                    .as_revealed_mut()
1992                    .expect("lower subtrie must exist")
1993                    .nodes
1994                    .get_mut(&path)
1995                    .expect("lower subtrie node must exist");
1996                // Lower subtrie root node hashes must be computed before updating upper subtrie
1997                // hashes
1998                debug_assert!(
1999                    node.hash().is_some(),
2000                    "Lower subtrie root node at path {path:?} has no hash"
2001                );
2002                node
2003            };
2004
2005            // Calculate the RLP node for the current node using upper subtrie
2006            self.upper_subtrie.inner.rlp_node(
2007                prefix_set,
2008                &mut update_actions_buf,
2009                stack_item,
2010                node,
2011                &self.branch_node_masks,
2012            );
2013        }
2014
2015        // If there were any branch node updates as a result of calculating the RLP node for the
2016        // upper trie then apply them to the top-level set.
2017        if let Some(mut update_actions_buf) = update_actions_buf {
2018            self.apply_subtrie_update_actions(
2019                #[allow(clippy::iter_with_drain)]
2020                update_actions_buf.drain(..),
2021            );
2022            self.update_actions_buffers.push(update_actions_buf);
2023        }
2024
2025        #[cfg(feature = "metrics")]
2026        self.metrics.subtrie_upper_hash_latency.record(start.elapsed());
2027
2028        debug_assert_eq!(self.upper_subtrie.inner.buffers.rlp_node_stack.len(), 1);
2029        self.upper_subtrie.inner.buffers.rlp_node_stack.pop().unwrap().rlp_node
2030    }
2031
2032    /// Returns:
2033    /// 1. List of lower [subtries](SparseSubtrie) that have changed according to the provided
2034    ///    [prefix set](PrefixSet). See documentation of [`ChangedSubtrie`] for more details. Lower
2035    ///    subtries whose root node is missing a hash will also be returned; this is required to
2036    ///    handle cases where extensions/leafs get shortened and therefore moved from the upper to a
2037    ///    lower subtrie.
2038    /// 2. Prefix set of keys that do not belong to any lower subtrie.
2039    ///
2040    /// This method helps optimize hash recalculations by identifying which specific
2041    /// lower subtries need to be updated. Each lower subtrie can then be updated in parallel.
2042    ///
2043    /// IMPORTANT: The method removes the subtries from `lower_subtries`, and the caller is
2044    /// responsible for returning them back into the array.
2045    #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all, fields(prefix_set_len = prefix_set.len()))]
2046    fn take_changed_lower_subtries(
2047        &mut self,
2048        prefix_set: &mut PrefixSet,
2049    ) -> (Vec<ChangedSubtrie>, PrefixSetMut) {
2050        // Fast-path: If the prefix set is empty then no subtries can have been changed. Just return
2051        // empty values.
2052        if prefix_set.is_empty() {
2053            return Default::default();
2054        }
2055
2056        // Clone the prefix set to iterate over its keys. Cloning is cheap, it's just an Arc.
2057        let prefix_set_clone = prefix_set.clone();
2058        let mut prefix_set_iter = prefix_set_clone.into_iter().copied().peekable();
2059        let mut changed_subtries = Vec::new();
2060        let mut unchanged_prefix_set = PrefixSetMut::default();
2061        let updates_enabled = self.updates_enabled();
2062
2063        for (index, subtrie) in self.lower_subtries.iter_mut().enumerate() {
2064            if let Some(subtrie) = subtrie.take_revealed_if(|subtrie| {
2065                prefix_set.contains(&subtrie.path) ||
2066                    subtrie.nodes.get(&subtrie.path).is_some_and(|n| n.hash().is_none())
2067            }) {
2068                let prefix_set = if prefix_set.all() {
2069                    unchanged_prefix_set = PrefixSetMut::all();
2070                    PrefixSetMut::all()
2071                } else {
2072                    // Take those keys from the original prefix set that start with the subtrie path
2073                    //
2074                    // Subtries are stored in the order of their paths, so we can use the same
2075                    // prefix set iterator.
2076                    let mut new_prefix_set = Vec::new();
2077                    while let Some(key) = prefix_set_iter.peek() {
2078                        if key.starts_with(&subtrie.path) {
2079                            // If the key starts with the subtrie path, add it to the new prefix set
2080                            new_prefix_set.push(prefix_set_iter.next().unwrap());
2081                        } else if new_prefix_set.is_empty() && key < &subtrie.path {
2082                            // If we didn't yet have any keys that belong to this subtrie, and the
2083                            // current key is still less than the subtrie path, add it to the
2084                            // unchanged prefix set
2085                            unchanged_prefix_set.insert(prefix_set_iter.next().unwrap());
2086                        } else {
2087                            // If we're past the subtrie path, we're done with this subtrie. Do not
2088                            // advance the iterator, the next key will be processed either by the
2089                            // next subtrie or inserted into the unchanged prefix set.
2090                            break
2091                        }
2092                    }
2093                    PrefixSetMut::from(new_prefix_set)
2094                }
2095                .freeze();
2096
2097                // We need the full path of root node of the lower subtrie to the unchanged prefix
2098                // set, so that we don't skip it when calculating hashes for the upper subtrie.
2099                match subtrie.nodes.get(&subtrie.path) {
2100                    Some(SparseNode::Extension { key, .. } | SparseNode::Leaf { key, .. }) => {
2101                        unchanged_prefix_set.insert(subtrie.path.join(key));
2102                    }
2103                    Some(SparseNode::Branch { .. }) => {
2104                        unchanged_prefix_set.insert(subtrie.path);
2105                    }
2106                    _ => {}
2107                }
2108
2109                let update_actions_buf =
2110                    updates_enabled.then(|| self.update_actions_buffers.pop().unwrap_or_default());
2111
2112                changed_subtries.push(ChangedSubtrie {
2113                    index,
2114                    subtrie,
2115                    prefix_set,
2116                    update_actions_buf,
2117                });
2118            }
2119        }
2120
2121        // Extend the unchanged prefix set with the remaining keys that are not part of any subtries
2122        unchanged_prefix_set.extend_keys(prefix_set_iter);
2123
2124        (changed_subtries, unchanged_prefix_set)
2125    }
2126
2127    /// Returns an iterator over all nodes in the trie in no particular order.
2128    #[cfg(test)]
2129    fn all_nodes(&self) -> impl IntoIterator<Item = (&Nibbles, &SparseNode)> {
2130        let mut nodes = vec![];
2131        for subtrie in self.lower_subtries.iter().filter_map(LowerSparseSubtrie::as_revealed_ref) {
2132            nodes.extend(subtrie.nodes.iter())
2133        }
2134        nodes.extend(self.upper_subtrie.nodes.iter());
2135        nodes
2136    }
2137
2138    /// Reveals a trie node in the upper trie if it has not been revealed before. When revealing
2139    /// branch/extension nodes this may recurse into a lower trie to reveal a child.
2140    ///
2141    /// This function decodes a trie node and inserts it into the trie structure. It handles
2142    /// different node types (leaf, extension, branch) by appropriately adding them to the trie and
2143    /// recursively revealing their children.
2144    ///
2145    /// # Arguments
2146    ///
2147    /// * `path` - The path where the node should be revealed
2148    /// * `node` - The trie node to reveal
2149    /// * `masks` - Branch node masks if known
2150    ///
2151    /// # Returns
2152    ///
2153    /// `Ok(())` if successful, or an error if the node was not revealed.
2154    fn reveal_upper_node(
2155        &mut self,
2156        path: Nibbles,
2157        node: &TrieNode,
2158        masks: Option<BranchNodeMasks>,
2159    ) -> SparseTrieResult<()> {
2160        // Only reveal nodes that can be reached given the current state of the upper trie. If they
2161        // can't be reached, it means that they were removed.
2162        if !self.is_path_reachable_from_upper(&path) {
2163            return Ok(())
2164        }
2165
2166        // Exit early if the node was already revealed before.
2167        if !self.upper_subtrie.reveal_node(path, node, masks)? {
2168            return Ok(())
2169        }
2170
2171        // The previous upper_trie.reveal_node call will not have revealed any child nodes via
2172        // reveal_node_or_hash if the child node would be found on a lower subtrie. We handle that
2173        // here by manually checking the specific cases where this could happen, and calling
2174        // reveal_node_or_hash for each.
2175        match node {
2176            TrieNode::Branch(branch) => {
2177                // If a branch is at the cutoff level of the trie then it will be in the upper trie,
2178                // but all of its children will be in a lower trie. Check if a child node would be
2179                // in the lower subtrie, and reveal accordingly.
2180                if !SparseSubtrieType::path_len_is_upper(path.len() + 1) {
2181                    let mut stack_ptr = branch.as_ref().first_child_index();
2182                    for idx in branch.state_mask.iter() {
2183                        let mut child_path = path;
2184                        child_path.push_unchecked(idx);
2185                        self.lower_subtrie_for_path_mut(&child_path)
2186                            .expect("child_path must have a lower subtrie")
2187                            .reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
2188                        stack_ptr += 1;
2189                    }
2190                }
2191            }
2192            TrieNode::Extension(ext) => {
2193                let mut child_path = path;
2194                child_path.extend(&ext.key);
2195                if let Some(subtrie) = self.lower_subtrie_for_path_mut(&child_path) {
2196                    subtrie.reveal_node_or_hash(child_path, &ext.child)?;
2197                }
2198            }
2199            TrieNode::EmptyRoot | TrieNode::Leaf(_) => (),
2200        }
2201
2202        Ok(())
2203    }
2204
2205    /// Return updated subtries back to the trie after executing any actions required on the
2206    /// top-level `SparseTrieUpdates`.
2207    #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all)]
2208    fn insert_changed_subtries(
2209        &mut self,
2210        changed_subtries: impl IntoIterator<Item = ChangedSubtrie>,
2211    ) {
2212        for ChangedSubtrie { index, subtrie, update_actions_buf, .. } in changed_subtries {
2213            if let Some(mut update_actions_buf) = update_actions_buf {
2214                self.apply_subtrie_update_actions(
2215                    #[allow(clippy::iter_with_drain)]
2216                    update_actions_buf.drain(..),
2217                );
2218                self.update_actions_buffers.push(update_actions_buf);
2219            }
2220
2221            self.lower_subtries[index] = LowerSparseSubtrie::Revealed(subtrie);
2222            self.subtrie_heat.mark_modified(index);
2223        }
2224    }
2225
2226    /// Returns a heuristic for the in-memory size of this trie in bytes.
2227    ///
2228    /// This is an approximation that accounts for:
2229    /// - The upper subtrie nodes and values
2230    /// - All revealed lower subtries nodes and values
2231    /// - The prefix set keys
2232    /// - The branch node masks map
2233    /// - Updates if retained
2234    /// - Update action buffers
2235    ///
2236    /// Note: Heap allocations for hash maps may be larger due to load factor overhead.
2237    pub fn memory_size(&self) -> usize {
2238        let mut size = core::mem::size_of::<Self>();
2239
2240        // Upper subtrie
2241        size += self.upper_subtrie.memory_size();
2242
2243        // Lower subtries (both Revealed and Blind with allocation)
2244        for subtrie in self.lower_subtries.iter() {
2245            size += subtrie.memory_size();
2246        }
2247
2248        // Prefix set keys
2249        size += self.prefix_set.len() * core::mem::size_of::<Nibbles>();
2250
2251        // Branch node masks map
2252        size += self.branch_node_masks.len() *
2253            (core::mem::size_of::<Nibbles>() + core::mem::size_of::<BranchNodeMasks>());
2254
2255        // Updates if present
2256        if let Some(updates) = &self.updates {
2257            size += updates.updated_nodes.len() *
2258                (core::mem::size_of::<Nibbles>() + core::mem::size_of::<BranchNodeCompact>());
2259            size += updates.removed_nodes.len() * core::mem::size_of::<Nibbles>();
2260        }
2261
2262        // Update actions buffers
2263        for buf in &self.update_actions_buffers {
2264            size += buf.capacity() * core::mem::size_of::<SparseTrieUpdatesAction>();
2265        }
2266
2267        size
2268    }
2269
2270    /// Determines if the given path can be directly reached from the upper trie.
2271    fn is_path_reachable_from_upper(&self, path: &Nibbles) -> bool {
2272        let mut current = Nibbles::default();
2273        while current.len() < path.len() {
2274            let Some(node) = self.upper_subtrie.nodes.get(&current) else { return false };
2275            match node {
2276                SparseNode::Branch { state_mask, .. } => {
2277                    if !state_mask.is_bit_set(path.get_unchecked(current.len())) {
2278                        return false
2279                    }
2280
2281                    current.push_unchecked(path.get_unchecked(current.len()));
2282                }
2283                SparseNode::Extension { key, .. } => {
2284                    if *key != path.slice(current.len()..current.len() + key.len()) {
2285                        return false
2286                    }
2287                    current.extend(key);
2288                }
2289                SparseNode::Hash(_) | SparseNode::Empty | SparseNode::Leaf { .. } => return false,
2290            }
2291        }
2292        true
2293    }
2294
2295    /// Checks if a boundary leaf (at `path.len() == UPPER_TRIE_MAX_DEPTH`) is reachable from its
2296    /// parent branch in the upper subtrie.
2297    ///
2298    /// This is used for leaves that sit at the upper/lower subtrie boundary, where the leaf is
2299    /// in a lower subtrie but its parent branch is in the upper subtrie.
2300    fn is_boundary_leaf_reachable(
2301        upper_nodes: &HashMap<Nibbles, SparseNode>,
2302        path: &Nibbles,
2303        node: &TrieNode,
2304    ) -> bool {
2305        debug_assert_eq!(path.len(), UPPER_TRIE_MAX_DEPTH);
2306
2307        if !matches!(node, TrieNode::Leaf(_)) {
2308            return true
2309        }
2310
2311        let parent_path = path.slice(..path.len() - 1);
2312        let leaf_nibble = path.get_unchecked(path.len() - 1);
2313
2314        match upper_nodes.get(&parent_path) {
2315            Some(SparseNode::Branch { state_mask, .. }) => state_mask.is_bit_set(leaf_nibble),
2316            _ => false,
2317        }
2318    }
2319
2320    /// Returns a bitset of all subtries that are reachable from the upper trie. If subtrie is not
2321    /// reachable it means that it does not exist.
2322    fn reachable_subtries(&self) -> SubtriesBitmap {
2323        let mut reachable = SubtriesBitmap::default();
2324
2325        let mut stack = Vec::new();
2326        stack.push(Nibbles::default());
2327
2328        while let Some(current) = stack.pop() {
2329            let Some(node) = self.upper_subtrie.nodes.get(&current) else { continue };
2330            match node {
2331                SparseNode::Branch { state_mask, .. } => {
2332                    for idx in state_mask.iter() {
2333                        let mut next = current;
2334                        next.push_unchecked(idx);
2335                        if next.len() >= UPPER_TRIE_MAX_DEPTH {
2336                            reachable.set(path_subtrie_index_unchecked(&next));
2337                        } else {
2338                            stack.push(next);
2339                        }
2340                    }
2341                }
2342                SparseNode::Extension { key, .. } => {
2343                    let mut next = current;
2344                    next.extend(key);
2345                    if next.len() >= UPPER_TRIE_MAX_DEPTH {
2346                        reachable.set(path_subtrie_index_unchecked(&next));
2347                    } else {
2348                        stack.push(next);
2349                    }
2350                }
2351                SparseNode::Hash(_) | SparseNode::Empty | SparseNode::Leaf { .. } => {}
2352            };
2353        }
2354
2355        reachable
2356    }
2357}
2358
2359/// Bitset tracking which of the 256 lower subtries were modified in the current cycle.
2360#[derive(Clone, Default, PartialEq, Eq, Debug)]
2361struct SubtriesBitmap(U256);
2362
2363impl SubtriesBitmap {
2364    /// Marks a subtrie index as modified.
2365    #[inline]
2366    fn set(&mut self, idx: usize) {
2367        debug_assert!(idx < NUM_LOWER_SUBTRIES);
2368        self.0.set_bit(idx, true);
2369    }
2370
2371    /// Returns whether a subtrie index is marked as modified.
2372    #[inline]
2373    fn get(&self, idx: usize) -> bool {
2374        debug_assert!(idx < NUM_LOWER_SUBTRIES);
2375        self.0.bit(idx)
2376    }
2377
2378    /// Clears all modification flags.
2379    #[inline]
2380    const fn clear(&mut self) {
2381        self.0 = U256::ZERO;
2382    }
2383}
2384
2385/// Tracks heat (modification frequency) for each of the 256 lower subtries.
2386///
2387/// Heat is used to avoid pruning frequently-modified subtries, which would cause
2388/// expensive re-reveal operations on subsequent updates.
2389///
2390/// - Heat is incremented by 2 when a subtrie is modified
2391/// - Heat decays by 1 each prune cycle for subtries not modified that cycle
2392/// - Subtries with heat > 0 are considered "hot" and skipped during pruning
2393#[derive(Clone, PartialEq, Eq, Debug)]
2394struct SubtrieModifications {
2395    /// Heat level (0-255) for each of the 256 lower subtries.
2396    heat: [u8; NUM_LOWER_SUBTRIES],
2397    /// Tracks which subtries were modified in the current cycle.
2398    modified: SubtriesBitmap,
2399}
2400
2401impl Default for SubtrieModifications {
2402    fn default() -> Self {
2403        Self { heat: [0; NUM_LOWER_SUBTRIES], modified: SubtriesBitmap::default() }
2404    }
2405}
2406
2407impl SubtrieModifications {
2408    /// Marks a subtrie as modified, incrementing its heat by 1.
2409    #[inline]
2410    fn mark_modified(&mut self, idx: usize) {
2411        debug_assert!(idx < NUM_LOWER_SUBTRIES);
2412        self.modified.set(idx);
2413        self.heat[idx] = self.heat[idx].saturating_add(1);
2414    }
2415
2416    /// Returns whether a subtrie is currently hot (heat > 0).
2417    #[inline]
2418    fn is_hot(&self, idx: usize) -> bool {
2419        debug_assert!(idx < NUM_LOWER_SUBTRIES);
2420        self.heat[idx] > 0
2421    }
2422
2423    /// Decays heat for subtries not modified this cycle and resets modification tracking.
2424    /// Called at the start of each prune cycle.
2425    fn decay_and_reset(&mut self) {
2426        for (idx, heat) in self.heat.iter_mut().enumerate() {
2427            if !self.modified.get(idx) {
2428                *heat = heat.saturating_sub(1);
2429            }
2430        }
2431        self.modified.clear();
2432    }
2433
2434    /// Clears all heat tracking state.
2435    const fn clear(&mut self) {
2436        self.heat = [0; NUM_LOWER_SUBTRIES];
2437        self.modified.clear();
2438    }
2439}
2440
2441/// This is a subtrie of the [`ParallelSparseTrie`] that contains a map from path to sparse trie
2442/// nodes.
2443#[derive(Clone, PartialEq, Eq, Debug, Default)]
2444pub struct SparseSubtrie {
2445    /// The root path of this subtrie.
2446    ///
2447    /// This is the _full_ path to this subtrie, meaning it includes the first
2448    /// [`UPPER_TRIE_MAX_DEPTH`] nibbles that we also use for indexing subtries in the
2449    /// [`ParallelSparseTrie`].
2450    ///
2451    /// There should be a node for this path in `nodes` map.
2452    pub(crate) path: Nibbles,
2453    /// The map from paths to sparse trie nodes within this subtrie.
2454    nodes: HashMap<Nibbles, SparseNode>,
2455    /// Subset of fields for mutable access while `nodes` field is also being mutably borrowed.
2456    inner: SparseSubtrieInner,
2457}
2458
2459/// Returned by the `find_next_to_leaf` method to indicate either that the leaf has been found,
2460/// traversal should be continued from the given path, or the leaf is not in the trie.
2461enum FindNextToLeafOutcome {
2462    /// `Found` indicates that the leaf was found at the given path.
2463    Found,
2464    /// `ContinueFrom` indicates that traversal should continue from the given path.
2465    ContinueFrom(Nibbles),
2466    /// `NotFound` indicates that there is no way to traverse to the leaf, as it is not in the
2467    /// trie.
2468    NotFound,
2469    /// `BlindedNode` indicates that the node is blinded with the contained hash and cannot be
2470    /// traversed.
2471    BlindedNode(B256),
2472}
2473
2474impl SparseSubtrie {
2475    /// Creates a new empty subtrie with the specified root path.
2476    pub(crate) fn new(path: Nibbles) -> Self {
2477        Self { path, ..Default::default() }
2478    }
2479
2480    /// Returns true if this subtrie has any nodes, false otherwise.
2481    pub(crate) fn is_empty(&self) -> bool {
2482        self.nodes.is_empty()
2483    }
2484
2485    /// Returns true if the current path and its child are both found in the same level.
2486    fn is_child_same_level(current_path: &Nibbles, child_path: &Nibbles) -> bool {
2487        let current_level = core::mem::discriminant(&SparseSubtrieType::from_path(current_path));
2488        let child_level = core::mem::discriminant(&SparseSubtrieType::from_path(child_path));
2489        current_level == child_level
2490    }
2491
2492    /// Checks if a leaf node at the given path is reachable from its parent branch node.
2493    ///
2494    /// Returns `true` if:
2495    /// - The path is at the root (no parent to check)
2496    /// - The parent branch node has the corresponding `state_mask` bit set for this leaf
2497    ///
2498    /// Returns `false` if the parent is a branch node that doesn't have the `state_mask` bit set
2499    /// for this leaf's nibble, meaning the leaf is not reachable.
2500    fn is_leaf_reachable_from_parent(&self, path: &Nibbles) -> bool {
2501        if path.is_empty() {
2502            return true
2503        }
2504
2505        let parent_path = path.slice(..path.len() - 1);
2506        let leaf_nibble = path.get_unchecked(path.len() - 1);
2507
2508        match self.nodes.get(&parent_path) {
2509            Some(SparseNode::Branch { state_mask, .. }) => state_mask.is_bit_set(leaf_nibble),
2510            _ => false,
2511        }
2512    }
2513
2514    /// Updates or inserts a leaf node at the specified key path with the provided RLP-encoded
2515    /// value.
2516    ///
2517    /// If the leaf did not previously exist, this method adjusts the trie structure by inserting
2518    /// new leaf nodes, splitting branch nodes, or collapsing extension nodes as needed.
2519    ///
2520    /// # Returns
2521    ///
2522    /// Returns the path and masks of any blinded node revealed as a result of updating the leaf.
2523    ///
2524    /// If an update requires revealing a blinded node, an error is returned if the blinded
2525    /// provider returns an error.
2526    ///
2527    /// This method is atomic: if an error occurs during structural changes, all modifications
2528    /// are rolled back and the trie state is unchanged.
2529    pub fn update_leaf(
2530        &mut self,
2531        full_path: Nibbles,
2532        value: Vec<u8>,
2533        provider: impl TrieNodeProvider,
2534        retain_updates: bool,
2535    ) -> SparseTrieResult<Option<(Nibbles, BranchNodeMasks)>> {
2536        debug_assert!(full_path.starts_with(&self.path));
2537
2538        // Check if value already exists - if so, just update it (no structural changes needed)
2539        if let Entry::Occupied(mut e) = self.inner.values.entry(full_path) {
2540            e.insert(value);
2541            return Ok(None)
2542        }
2543
2544        // Here we are starting at the root of the subtrie, and traversing from there.
2545        let mut current = Some(self.path);
2546        let mut revealed = None;
2547
2548        // Track inserted nodes and modified original for rollback on error
2549        let mut inserted_nodes: Vec<Nibbles> = Vec::new();
2550        let mut modified_original: Option<(Nibbles, SparseNode)> = None;
2551
2552        while let Some(current_path) = current {
2553            // Save original node for potential rollback (only if not already saved)
2554            if modified_original.is_none() &&
2555                let Some(node) = self.nodes.get(&current_path)
2556            {
2557                modified_original = Some((current_path, node.clone()));
2558            }
2559
2560            let step_result = self.update_next_node(current_path, &full_path, retain_updates);
2561
2562            // Handle errors from update_next_node - rollback and propagate
2563            if let Err(e) = step_result {
2564                self.rollback_leaf_insert(&full_path, &inserted_nodes, modified_original.take());
2565                return Err(e);
2566            }
2567
2568            match step_result? {
2569                LeafUpdateStep::Continue { next_node } => {
2570                    current = Some(next_node);
2571                    // Clear modified_original since we haven't actually modified anything yet
2572                    modified_original = None;
2573                }
2574                LeafUpdateStep::Complete { inserted_nodes: new_inserted, reveal_path } => {
2575                    inserted_nodes.extend(new_inserted);
2576
2577                    if let Some(reveal_path) = reveal_path &&
2578                        self.nodes.get(&reveal_path).expect("node must exist").is_hash()
2579                    {
2580                        debug!(
2581                            target: "trie::parallel_sparse",
2582                            child_path = ?reveal_path,
2583                            leaf_full_path = ?full_path,
2584                            "Extension node child not revealed in update_leaf, falling back to db",
2585                        );
2586                        let revealed_node = match provider.trie_node(&reveal_path) {
2587                            Ok(node) => node,
2588                            Err(e) => {
2589                                self.rollback_leaf_insert(
2590                                    &full_path,
2591                                    &inserted_nodes,
2592                                    modified_original.take(),
2593                                );
2594                                return Err(e);
2595                            }
2596                        };
2597                        if let Some(RevealedNode { node, tree_mask, hash_mask }) = revealed_node {
2598                            let decoded = match TrieNode::decode(&mut &node[..]) {
2599                                Ok(d) => d,
2600                                Err(e) => {
2601                                    self.rollback_leaf_insert(
2602                                        &full_path,
2603                                        &inserted_nodes,
2604                                        modified_original.take(),
2605                                    );
2606                                    return Err(e.into());
2607                                }
2608                            };
2609                            trace!(
2610                                target: "trie::parallel_sparse",
2611                                ?reveal_path,
2612                                ?decoded,
2613                                ?tree_mask,
2614                                ?hash_mask,
2615                                "Revealing child (from lower)",
2616                            );
2617                            let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
2618                            if let Err(e) = self.reveal_node(reveal_path, &decoded, masks) {
2619                                self.rollback_leaf_insert(
2620                                    &full_path,
2621                                    &inserted_nodes,
2622                                    modified_original.take(),
2623                                );
2624                                return Err(e);
2625                            }
2626
2627                            debug_assert_eq!(
2628                                revealed, None,
2629                                "Only a single blinded node should be revealed during update_leaf"
2630                            );
2631                            revealed = masks.map(|masks| (reveal_path, masks));
2632                        } else {
2633                            self.rollback_leaf_insert(
2634                                &full_path,
2635                                &inserted_nodes,
2636                                modified_original.take(),
2637                            );
2638                            return Err(SparseTrieErrorKind::NodeNotFoundInProvider {
2639                                path: reveal_path,
2640                            }
2641                            .into())
2642                        }
2643                    }
2644
2645                    current = None;
2646                }
2647                LeafUpdateStep::NodeNotFound => {
2648                    current = None;
2649                }
2650            }
2651        }
2652
2653        // Only insert the value after all structural changes succeed
2654        self.inner.values.insert(full_path, value);
2655
2656        Ok(revealed)
2657    }
2658
2659    /// Rollback structural changes made during a failed leaf insert.
2660    ///
2661    /// This removes any nodes that were inserted and restores the original node
2662    /// that was modified, ensuring atomicity of `update_leaf`.
2663    fn rollback_leaf_insert(
2664        &mut self,
2665        full_path: &Nibbles,
2666        inserted_nodes: &[Nibbles],
2667        modified_original: Option<(Nibbles, SparseNode)>,
2668    ) {
2669        // Remove any values that may have been inserted
2670        self.inner.values.remove(full_path);
2671
2672        // Remove all inserted nodes
2673        for node_path in inserted_nodes {
2674            self.nodes.remove(node_path);
2675        }
2676
2677        // Restore the original node that was modified
2678        if let Some((path, original_node)) = modified_original {
2679            self.nodes.insert(path, original_node);
2680        }
2681    }
2682
2683    /// Processes the current node, returning what to do next in the leaf update process.
2684    ///
2685    /// This will add or update any nodes in the trie as necessary.
2686    ///
2687    /// Returns a `LeafUpdateStep` containing the next node to process (if any) and
2688    /// the paths of nodes that were inserted during this step.
2689    fn update_next_node(
2690        &mut self,
2691        mut current: Nibbles,
2692        path: &Nibbles,
2693        retain_updates: bool,
2694    ) -> SparseTrieResult<LeafUpdateStep> {
2695        debug_assert!(path.starts_with(&self.path));
2696        debug_assert!(current.starts_with(&self.path));
2697        debug_assert!(path.starts_with(&current));
2698        let Some(node) = self.nodes.get_mut(&current) else {
2699            return Ok(LeafUpdateStep::NodeNotFound);
2700        };
2701        match node {
2702            SparseNode::Empty => {
2703                // We need to insert the node with a different path and key depending on the path of
2704                // the subtrie.
2705                let path = path.slice(self.path.len()..);
2706                *node = SparseNode::new_leaf(path);
2707                Ok(LeafUpdateStep::complete_with_insertions(vec![current], None))
2708            }
2709            SparseNode::Hash(hash) => {
2710                Err(SparseTrieErrorKind::BlindedNode { path: current, hash: *hash }.into())
2711            }
2712            SparseNode::Leaf { key: current_key, .. } => {
2713                current.extend(current_key);
2714
2715                // this leaf is being updated
2716                debug_assert!(
2717                    &current != path,
2718                    "we already checked leaf presence in the beginning"
2719                );
2720
2721                // find the common prefix
2722                let common = current.common_prefix_length(path);
2723
2724                // update existing node
2725                let new_ext_key = current.slice(current.len() - current_key.len()..common);
2726                *node = SparseNode::new_ext(new_ext_key);
2727
2728                // create a branch node and corresponding leaves
2729                self.nodes.reserve(3);
2730                let branch_path = current.slice(..common);
2731                let new_leaf_path = path.slice(..=common);
2732                let existing_leaf_path = current.slice(..=common);
2733
2734                self.nodes.insert(
2735                    branch_path,
2736                    SparseNode::new_split_branch(
2737                        current.get_unchecked(common),
2738                        path.get_unchecked(common),
2739                    ),
2740                );
2741                self.nodes.insert(new_leaf_path, SparseNode::new_leaf(path.slice(common + 1..)));
2742                self.nodes
2743                    .insert(existing_leaf_path, SparseNode::new_leaf(current.slice(common + 1..)));
2744
2745                Ok(LeafUpdateStep::complete_with_insertions(
2746                    vec![branch_path, new_leaf_path, existing_leaf_path],
2747                    None,
2748                ))
2749            }
2750            SparseNode::Extension { key, .. } => {
2751                current.extend(key);
2752
2753                if !path.starts_with(&current) {
2754                    // find the common prefix
2755                    let common = current.common_prefix_length(path);
2756                    *key = current.slice(current.len() - key.len()..common);
2757
2758                    // If branch node updates retention is enabled, we need to query the
2759                    // extension node child to later set the hash mask for a parent branch node
2760                    // correctly.
2761                    let reveal_path = retain_updates.then_some(current);
2762
2763                    // create state mask for new branch node
2764                    // NOTE: this might overwrite the current extension node
2765                    self.nodes.reserve(3);
2766                    let branch_path = current.slice(..common);
2767                    let new_leaf_path = path.slice(..=common);
2768                    let branch = SparseNode::new_split_branch(
2769                        current.get_unchecked(common),
2770                        path.get_unchecked(common),
2771                    );
2772
2773                    self.nodes.insert(branch_path, branch);
2774
2775                    // create new leaf
2776                    let new_leaf = SparseNode::new_leaf(path.slice(common + 1..));
2777                    self.nodes.insert(new_leaf_path, new_leaf);
2778
2779                    let mut inserted_nodes = vec![branch_path, new_leaf_path];
2780
2781                    // recreate extension to previous child if needed
2782                    let key = current.slice(common + 1..);
2783                    if !key.is_empty() {
2784                        let ext_path = current.slice(..=common);
2785                        self.nodes.insert(ext_path, SparseNode::new_ext(key));
2786                        inserted_nodes.push(ext_path);
2787                    }
2788
2789                    return Ok(LeafUpdateStep::complete_with_insertions(inserted_nodes, reveal_path))
2790                }
2791
2792                Ok(LeafUpdateStep::continue_with(current))
2793            }
2794            SparseNode::Branch { state_mask, .. } => {
2795                let nibble = path.get_unchecked(current.len());
2796                current.push_unchecked(nibble);
2797                if !state_mask.is_bit_set(nibble) {
2798                    state_mask.set_bit(nibble);
2799                    let new_leaf = SparseNode::new_leaf(path.slice(current.len()..));
2800                    self.nodes.insert(current, new_leaf);
2801                    return Ok(LeafUpdateStep::complete_with_insertions(vec![current], None))
2802                }
2803
2804                // If the nibble is set, we can continue traversing the branch.
2805                Ok(LeafUpdateStep::continue_with(current))
2806            }
2807        }
2808    }
2809
2810    /// Internal implementation of the method of the same name on `ParallelSparseTrie`.
2811    fn reveal_node(
2812        &mut self,
2813        path: Nibbles,
2814        node: &TrieNode,
2815        masks: Option<BranchNodeMasks>,
2816    ) -> SparseTrieResult<bool> {
2817        debug_assert!(path.starts_with(&self.path));
2818
2819        // If the node is already revealed and it's not a hash node, do nothing.
2820        if self.nodes.get(&path).is_some_and(|node| !node.is_hash()) {
2821            return Ok(false)
2822        }
2823
2824        trace!(
2825            target: "trie::parallel_sparse",
2826            ?path,
2827            ?node,
2828            ?masks,
2829            "Revealing node",
2830        );
2831
2832        match node {
2833            TrieNode::EmptyRoot => {
2834                // For an empty root, ensure that we are at the root path, and at the upper subtrie.
2835                debug_assert!(path.is_empty());
2836                debug_assert!(self.path.is_empty());
2837                self.nodes.insert(path, SparseNode::Empty);
2838            }
2839            TrieNode::Branch(branch) => {
2840                // Update the branch node entry in the nodes map, handling cases where a blinded
2841                // node is now replaced with a revealed node.
2842                match self.nodes.entry(path) {
2843                    Entry::Occupied(mut entry) => match entry.get() {
2844                        // Replace a hash node with a fully revealed branch node.
2845                        SparseNode::Hash(hash) => {
2846                            entry.insert(SparseNode::Branch {
2847                                state_mask: branch.state_mask,
2848                                // Memoize the hash of a previously blinded node in a new branch
2849                                // node.
2850                                hash: Some(*hash),
2851                                store_in_db_trie: Some(masks.is_some_and(|m| {
2852                                    !m.hash_mask.is_empty() || !m.tree_mask.is_empty()
2853                                })),
2854                            });
2855                        }
2856                        _ => unreachable!("checked that node is either a hash or non-existent"),
2857                    },
2858                    Entry::Vacant(entry) => {
2859                        entry.insert(SparseNode::new_branch(branch.state_mask));
2860                    }
2861                }
2862
2863                // For a branch node, iterate over all children. This must happen second so leaf
2864                // children can check connectivity with parent branch.
2865                let mut stack_ptr = branch.as_ref().first_child_index();
2866                for idx in branch.state_mask.iter() {
2867                    let mut child_path = path;
2868                    child_path.push_unchecked(idx);
2869                    if Self::is_child_same_level(&path, &child_path) {
2870                        // Reveal each child node or hash it has, but only if the child is on
2871                        // the same level as the parent.
2872                        self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?;
2873                    }
2874                    stack_ptr += 1;
2875                }
2876            }
2877            TrieNode::Extension(ext) => match self.nodes.entry(path) {
2878                Entry::Occupied(mut entry) => match entry.get() {
2879                    // Replace a hash node with a revealed extension node.
2880                    SparseNode::Hash(hash) => {
2881                        let mut child_path = *entry.key();
2882                        child_path.extend(&ext.key);
2883                        entry.insert(SparseNode::Extension {
2884                            key: ext.key,
2885                            // Memoize the hash of a previously blinded node in a new extension
2886                            // node.
2887                            hash: Some(*hash),
2888                            store_in_db_trie: None,
2889                        });
2890                        if Self::is_child_same_level(&path, &child_path) {
2891                            self.reveal_node_or_hash(child_path, &ext.child)?;
2892                        }
2893                    }
2894                    _ => unreachable!("checked that node is either a hash or non-existent"),
2895                },
2896                Entry::Vacant(entry) => {
2897                    let mut child_path = *entry.key();
2898                    child_path.extend(&ext.key);
2899                    entry.insert(SparseNode::new_ext(ext.key));
2900                    if Self::is_child_same_level(&path, &child_path) {
2901                        self.reveal_node_or_hash(child_path, &ext.child)?;
2902                    }
2903                }
2904            },
2905            TrieNode::Leaf(leaf) => {
2906                // Skip the reachability check when path.len() == UPPER_TRIE_MAX_DEPTH because
2907                // at that boundary the leaf is in the lower subtrie but its parent branch is in
2908                // the upper subtrie. The subtrie cannot check connectivity across the upper/lower
2909                // boundary, so that check happens in `reveal_nodes` instead.
2910                if path.len() != UPPER_TRIE_MAX_DEPTH && !self.is_leaf_reachable_from_parent(&path)
2911                {
2912                    trace!(
2913                        target: "trie::parallel_sparse",
2914                        ?path,
2915                        "Leaf not reachable from parent branch, skipping",
2916                    );
2917                    return Ok(false)
2918                }
2919
2920                let mut full_key = path;
2921                full_key.extend(&leaf.key);
2922
2923                match self.inner.values.entry(full_key) {
2924                    Entry::Occupied(_) => {
2925                        trace!(
2926                            target: "trie::parallel_sparse",
2927                            ?path,
2928                            ?full_key,
2929                            "Leaf full key value already present, skipping",
2930                        );
2931                        return Ok(false)
2932                    }
2933                    Entry::Vacant(entry) => {
2934                        entry.insert(leaf.value.clone());
2935                    }
2936                }
2937
2938                match self.nodes.entry(path) {
2939                    Entry::Occupied(mut entry) => match entry.get() {
2940                        // Replace a hash node with a revealed leaf node and store leaf node value.
2941                        SparseNode::Hash(hash) => {
2942                            entry.insert(SparseNode::Leaf {
2943                                key: leaf.key,
2944                                // Memoize the hash of a previously blinded node in a new leaf
2945                                // node.
2946                                hash: Some(*hash),
2947                            });
2948                        }
2949                        _ => unreachable!("checked that node is either a hash or non-existent"),
2950                    },
2951                    Entry::Vacant(entry) => {
2952                        entry.insert(SparseNode::new_leaf(leaf.key));
2953                    }
2954                }
2955            }
2956        }
2957
2958        Ok(true)
2959    }
2960
2961    /// Reveals either a node or its hash placeholder based on the provided child data.
2962    ///
2963    /// When traversing the trie, we often encounter references to child nodes that
2964    /// are either directly embedded or represented by their hash. This method
2965    /// handles both cases:
2966    ///
2967    /// 1. If the child data represents a hash (32+1=33 bytes), store it as a hash node
2968    /// 2. Otherwise, decode the data as a [`TrieNode`] and recursively reveal it using
2969    ///    `reveal_node`
2970    ///
2971    /// # Returns
2972    ///
2973    /// Returns `Ok(())` if successful, or an error if the node cannot be revealed.
2974    ///
2975    /// # Error Handling
2976    ///
2977    /// Will error if there's a conflict between a new hash node and an existing one
2978    /// at the same path
2979    fn reveal_node_or_hash(&mut self, path: Nibbles, child: &[u8]) -> SparseTrieResult<()> {
2980        if child.len() == B256::len_bytes() + 1 {
2981            let hash = B256::from_slice(&child[1..]);
2982            match self.nodes.entry(path) {
2983                Entry::Occupied(entry) => match entry.get() {
2984                    // Hash node with a different hash can't be handled.
2985                    SparseNode::Hash(previous_hash) if previous_hash != &hash => {
2986                        return Err(SparseTrieErrorKind::Reveal {
2987                            path: *entry.key(),
2988                            node: Box::new(SparseNode::Hash(hash)),
2989                        }
2990                        .into())
2991                    }
2992                    _ => {}
2993                },
2994                Entry::Vacant(entry) => {
2995                    entry.insert(SparseNode::Hash(hash));
2996                }
2997            }
2998            return Ok(())
2999        }
3000
3001        self.reveal_node(path, &TrieNode::decode(&mut &child[..])?, None)?;
3002
3003        Ok(())
3004    }
3005
3006    /// Recalculates and updates the RLP hashes for the changed nodes in this subtrie.
3007    ///
3008    /// The function starts from the subtrie root, traverses down to leaves, and then calculates
3009    /// the hashes from leaves back up to the root. It uses a stack from [`SparseSubtrieBuffers`] to
3010    /// track the traversal and accumulate RLP encodings.
3011    ///
3012    /// # Parameters
3013    ///
3014    /// - `prefix_set`: The set of trie paths whose nodes have changed.
3015    /// - `update_actions`: A buffer which `SparseTrieUpdatesAction`s will be written to in the
3016    ///   event that any changes to the top-level updates are required. If None then update
3017    ///   retention is disabled.
3018    /// - `branch_node_masks`: The tree and hash masks for branch nodes.
3019    ///
3020    /// # Returns
3021    ///
3022    /// A tuple containing the root node of the updated subtrie.
3023    ///
3024    /// # Panics
3025    ///
3026    /// If the node at the root path does not exist.
3027    #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all, fields(root = ?self.path), ret)]
3028    fn update_hashes(
3029        &mut self,
3030        prefix_set: &mut PrefixSet,
3031        update_actions: &mut Option<Vec<SparseTrieUpdatesAction>>,
3032        branch_node_masks: &BranchNodeMasksMap,
3033    ) -> RlpNode {
3034        trace!(target: "trie::parallel_sparse", "Updating subtrie hashes");
3035
3036        debug_assert!(prefix_set.iter().all(|path| path.starts_with(&self.path)));
3037
3038        debug_assert!(self.inner.buffers.path_stack.is_empty());
3039        self.inner
3040            .buffers
3041            .path_stack
3042            .push(RlpNodePathStackItem { path: self.path, is_in_prefix_set: None });
3043
3044        while let Some(stack_item) = self.inner.buffers.path_stack.pop() {
3045            let path = stack_item.path;
3046            let node = self
3047                .nodes
3048                .get_mut(&path)
3049                .unwrap_or_else(|| panic!("node at path {path:?} does not exist"));
3050
3051            self.inner.rlp_node(prefix_set, update_actions, stack_item, node, branch_node_masks);
3052        }
3053
3054        debug_assert_eq!(self.inner.buffers.rlp_node_stack.len(), 1);
3055        self.inner.buffers.rlp_node_stack.pop().unwrap().rlp_node
3056    }
3057
3058    /// Removes all nodes and values from the subtrie, resetting it to a blank state
3059    /// with only an empty root node. This is used when a storage root is deleted.
3060    fn wipe(&mut self) {
3061        self.nodes = HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]);
3062        self.inner.clear();
3063    }
3064
3065    /// Clears the subtrie, keeping the data structures allocated.
3066    pub(crate) fn clear(&mut self) {
3067        self.nodes.clear();
3068        self.inner.clear();
3069    }
3070
3071    /// Shrinks the capacity of the subtrie's node storage.
3072    pub(crate) fn shrink_nodes_to(&mut self, size: usize) {
3073        self.nodes.shrink_to(size);
3074    }
3075
3076    /// Shrinks the capacity of the subtrie's value storage.
3077    pub(crate) fn shrink_values_to(&mut self, size: usize) {
3078        self.inner.values.shrink_to(size);
3079    }
3080
3081    /// Returns a heuristic for the in-memory size of this subtrie in bytes.
3082    pub(crate) fn memory_size(&self) -> usize {
3083        let mut size = core::mem::size_of::<Self>();
3084
3085        // Nodes map: key (Nibbles) + value (SparseNode)
3086        for (path, node) in &self.nodes {
3087            size += core::mem::size_of::<Nibbles>();
3088            size += path.len(); // Nibbles heap allocation
3089            size += node.memory_size();
3090        }
3091
3092        // Values map: key (Nibbles) + value (Vec<u8>)
3093        for (path, value) in &self.inner.values {
3094            size += core::mem::size_of::<Nibbles>();
3095            size += path.len(); // Nibbles heap allocation
3096            size += core::mem::size_of::<Vec<u8>>() + value.capacity();
3097        }
3098
3099        // Buffers
3100        size += self.inner.buffers.memory_size();
3101
3102        size
3103    }
3104}
3105
3106/// Helper type for [`SparseSubtrie`] to mutably access only a subset of fields from the original
3107/// struct.
3108#[derive(Clone, PartialEq, Eq, Debug, Default)]
3109struct SparseSubtrieInner {
3110    /// Map from leaf key paths to their values.
3111    /// All values are stored here instead of directly in leaf nodes.
3112    values: HashMap<Nibbles, Vec<u8>>,
3113    /// Reusable buffers for [`SparseSubtrie::update_hashes`].
3114    buffers: SparseSubtrieBuffers,
3115}
3116
3117impl SparseSubtrieInner {
3118    /// Computes the RLP encoding and its hash for a single (trie node)[`SparseNode`].
3119    ///
3120    /// # Deferred Processing
3121    ///
3122    /// When an extension or a branch node depends on child nodes that haven't been computed yet,
3123    /// the function pushes the current node back onto the path stack along with its children,
3124    /// then returns early. This allows the iterative algorithm to process children first before
3125    /// retrying the parent.
3126    ///
3127    /// # Parameters
3128    ///
3129    /// - `prefix_set`: Set of prefixes (key paths) that have been marked as updated
3130    /// - `update_actions`: A buffer which `SparseTrieUpdatesAction`s will be written to in the
3131    ///   event that any changes to the top-level updates are required. If None then update
3132    ///   retention is disabled.
3133    /// - `stack_item`: The stack item to process
3134    /// - `node`: The sparse node to process (will be mutated to update hash)
3135    /// - `branch_node_masks`: The tree and hash masks for branch nodes.
3136    ///
3137    /// # Side Effects
3138    ///
3139    /// - Updates the node's hash field after computing RLP
3140    /// - Pushes nodes to [`SparseSubtrieBuffers::path_stack`] to manage traversal
3141    /// - May push items onto the path stack for deferred processing
3142    ///
3143    /// # Exit condition
3144    ///
3145    /// Once all nodes have been processed and all RLPs and hashes calculated, pushes the root node
3146    /// onto the [`SparseSubtrieBuffers::rlp_node_stack`] and exits.
3147    fn rlp_node(
3148        &mut self,
3149        prefix_set: &mut PrefixSet,
3150        update_actions: &mut Option<Vec<SparseTrieUpdatesAction>>,
3151        mut stack_item: RlpNodePathStackItem,
3152        node: &mut SparseNode,
3153        branch_node_masks: &BranchNodeMasksMap,
3154    ) {
3155        let path = stack_item.path;
3156        trace!(
3157            target: "trie::parallel_sparse",
3158            ?path,
3159            ?node,
3160            "Calculating node RLP"
3161        );
3162
3163        // Check if the path is in the prefix set.
3164        // First, check the cached value. If it's `None`, then check the prefix set, and update
3165        // the cached value.
3166        let mut prefix_set_contains = |path: &Nibbles| {
3167            *stack_item.is_in_prefix_set.get_or_insert_with(|| prefix_set.contains(path))
3168        };
3169
3170        let (rlp_node, node_type) = match node {
3171            SparseNode::Empty => (RlpNode::word_rlp(&EMPTY_ROOT_HASH), SparseNodeType::Empty),
3172            SparseNode::Hash(hash) => {
3173                // Return pre-computed hash of a blinded node immediately
3174                (RlpNode::word_rlp(hash), SparseNodeType::Hash)
3175            }
3176            SparseNode::Leaf { key, hash } => {
3177                let mut path = path;
3178                path.extend(key);
3179                let value = self.values.get(&path);
3180                if let Some(hash) = hash.filter(|_| !prefix_set_contains(&path) || value.is_none())
3181                {
3182                    // If the node hash is already computed, and either the node path is not in
3183                    // the prefix set or the leaf doesn't belong to the current trie (its value is
3184                    // absent), return the pre-computed hash
3185                    (RlpNode::word_rlp(&hash), SparseNodeType::Leaf)
3186                } else {
3187                    // Encode the leaf node and update its hash
3188                    let value = self.values.get(&path).unwrap();
3189                    self.buffers.rlp_buf.clear();
3190                    let rlp_node = LeafNodeRef { key, value }.rlp(&mut self.buffers.rlp_buf);
3191                    *hash = rlp_node.as_hash();
3192                    trace!(
3193                        target: "trie::parallel_sparse",
3194                        ?path,
3195                        ?key,
3196                        value = %alloy_primitives::hex::encode(value),
3197                        ?hash,
3198                        "Calculated leaf hash",
3199                    );
3200                    (rlp_node, SparseNodeType::Leaf)
3201                }
3202            }
3203            SparseNode::Extension { key, hash, store_in_db_trie } => {
3204                let mut child_path = path;
3205                child_path.extend(key);
3206                if let Some((hash, store_in_db_trie)) =
3207                    hash.zip(*store_in_db_trie).filter(|_| !prefix_set_contains(&path))
3208                {
3209                    // If the node hash is already computed, and the node path is not in
3210                    // the prefix set, return the pre-computed hash
3211                    (
3212                        RlpNode::word_rlp(&hash),
3213                        SparseNodeType::Extension { store_in_db_trie: Some(store_in_db_trie) },
3214                    )
3215                } else if self.buffers.rlp_node_stack.last().is_some_and(|e| e.path == child_path) {
3216                    // Top of the stack has the child node, we can encode the extension node and
3217                    // update its hash
3218                    let RlpNodeStackItem { path: _, rlp_node: child, node_type: child_node_type } =
3219                        self.buffers.rlp_node_stack.pop().unwrap();
3220                    self.buffers.rlp_buf.clear();
3221                    let rlp_node =
3222                        ExtensionNodeRef::new(key, &child).rlp(&mut self.buffers.rlp_buf);
3223                    *hash = rlp_node.as_hash();
3224
3225                    let store_in_db_trie_value = child_node_type.store_in_db_trie();
3226
3227                    trace!(
3228                        target: "trie::parallel_sparse",
3229                        ?path,
3230                        ?child_path,
3231                        ?child_node_type,
3232                        "Extension node"
3233                    );
3234
3235                    *store_in_db_trie = store_in_db_trie_value;
3236
3237                    (
3238                        rlp_node,
3239                        SparseNodeType::Extension {
3240                            // Inherit the `store_in_db_trie` flag from the child node, which is
3241                            // always the branch node
3242                            store_in_db_trie: store_in_db_trie_value,
3243                        },
3244                    )
3245                } else {
3246                    // Need to defer processing until child is computed, on the next
3247                    // invocation update the node's hash.
3248                    self.buffers.path_stack.extend([
3249                        RlpNodePathStackItem {
3250                            path,
3251                            is_in_prefix_set: Some(prefix_set_contains(&path)),
3252                        },
3253                        RlpNodePathStackItem { path: child_path, is_in_prefix_set: None },
3254                    ]);
3255                    return
3256                }
3257            }
3258            SparseNode::Branch { state_mask, hash, store_in_db_trie } => {
3259                if let Some((hash, store_in_db_trie)) =
3260                    hash.zip(*store_in_db_trie).filter(|_| !prefix_set_contains(&path))
3261                {
3262                    let rlp_node = RlpNode::word_rlp(&hash);
3263                    let node_type =
3264                        SparseNodeType::Branch { store_in_db_trie: Some(store_in_db_trie) };
3265
3266                    trace!(
3267                        target: "trie::parallel_sparse",
3268                        ?path,
3269                        ?node_type,
3270                        ?rlp_node,
3271                        "Adding node to RLP node stack (cached branch)"
3272                    );
3273
3274                    // If the node hash is already computed, and the node path is not in
3275                    // the prefix set, return the pre-computed hash
3276                    self.buffers.rlp_node_stack.push(RlpNodeStackItem {
3277                        path,
3278                        rlp_node,
3279                        node_type,
3280                    });
3281                    return
3282                }
3283
3284                let retain_updates = update_actions.is_some() && prefix_set_contains(&path);
3285
3286                self.buffers.branch_child_buf.clear();
3287                // Walk children in a reverse order from `f` to `0`, so we pop the `0` first
3288                // from the stack and keep walking in the sorted order.
3289                for bit in state_mask.iter().rev() {
3290                    let mut child = path;
3291                    child.push_unchecked(bit);
3292                    self.buffers.branch_child_buf.push(child);
3293                }
3294
3295                self.buffers
3296                    .branch_value_stack_buf
3297                    .resize(self.buffers.branch_child_buf.len(), Default::default());
3298                let mut added_children = false;
3299
3300                let mut tree_mask = TrieMask::default();
3301                let mut hash_mask = TrieMask::default();
3302                let mut hashes = Vec::new();
3303
3304                // Lazy lookup for branch node masks - shared across loop iterations
3305                let mut path_masks_storage = None;
3306                let mut path_masks =
3307                    || *path_masks_storage.get_or_insert_with(|| branch_node_masks.get(&path));
3308
3309                for (i, child_path) in self.buffers.branch_child_buf.iter().enumerate() {
3310                    if self.buffers.rlp_node_stack.last().is_some_and(|e| &e.path == child_path) {
3311                        let RlpNodeStackItem {
3312                            path: _,
3313                            rlp_node: child,
3314                            node_type: child_node_type,
3315                        } = self.buffers.rlp_node_stack.pop().unwrap();
3316
3317                        // Update the masks only if we need to retain trie updates
3318                        if retain_updates {
3319                            // SAFETY: it's a child, so it's never empty
3320                            let last_child_nibble = child_path.last().unwrap();
3321
3322                            // Determine whether we need to set trie mask bit.
3323                            let should_set_tree_mask_bit = if let Some(store_in_db_trie) =
3324                                child_node_type.store_in_db_trie()
3325                            {
3326                                // A branch or an extension node explicitly set the
3327                                // `store_in_db_trie` flag
3328                                store_in_db_trie
3329                            } else {
3330                                // A blinded node has the tree mask bit set
3331                                child_node_type.is_hash() &&
3332                                    path_masks().is_some_and(|masks| {
3333                                        masks.tree_mask.is_bit_set(last_child_nibble)
3334                                    })
3335                            };
3336                            if should_set_tree_mask_bit {
3337                                tree_mask.set_bit(last_child_nibble);
3338                            }
3339
3340                            // Set the hash mask. If a child node is a revealed branch node OR
3341                            // is a blinded node that has its hash mask bit set according to the
3342                            // database, set the hash mask bit and save the hash.
3343                            let hash = child.as_hash().filter(|_| {
3344                                child_node_type.is_branch() ||
3345                                    (child_node_type.is_hash() &&
3346                                        path_masks().is_some_and(|masks| {
3347                                            masks.hash_mask.is_bit_set(last_child_nibble)
3348                                        }))
3349                            });
3350                            if let Some(hash) = hash {
3351                                hash_mask.set_bit(last_child_nibble);
3352                                hashes.push(hash);
3353                            }
3354                        }
3355
3356                        // Insert children in the resulting buffer in a normal order,
3357                        // because initially we iterated in reverse.
3358                        // SAFETY: i < len and len is never 0
3359                        let original_idx = self.buffers.branch_child_buf.len() - i - 1;
3360                        self.buffers.branch_value_stack_buf[original_idx] = child;
3361                        added_children = true;
3362                    } else {
3363                        // Need to defer processing until children are computed, on the next
3364                        // invocation update the node's hash.
3365                        debug_assert!(!added_children);
3366                        self.buffers.path_stack.push(RlpNodePathStackItem {
3367                            path,
3368                            is_in_prefix_set: Some(prefix_set_contains(&path)),
3369                        });
3370                        self.buffers.path_stack.extend(
3371                            self.buffers
3372                                .branch_child_buf
3373                                .drain(..)
3374                                .map(|path| RlpNodePathStackItem { path, is_in_prefix_set: None }),
3375                        );
3376                        return
3377                    }
3378                }
3379
3380                trace!(
3381                    target: "trie::parallel_sparse",
3382                    ?path,
3383                    ?tree_mask,
3384                    ?hash_mask,
3385                    "Branch node masks"
3386                );
3387
3388                // Top of the stack has all children node, we can encode the branch node and
3389                // update its hash
3390                self.buffers.rlp_buf.clear();
3391                let branch_node_ref =
3392                    BranchNodeRef::new(&self.buffers.branch_value_stack_buf, *state_mask);
3393                let rlp_node = branch_node_ref.rlp(&mut self.buffers.rlp_buf);
3394                *hash = rlp_node.as_hash();
3395
3396                // Save a branch node update only if it's not a root node, and we need to
3397                // persist updates.
3398                let store_in_db_trie_value = if let Some(update_actions) =
3399                    update_actions.as_mut().filter(|_| retain_updates && !path.is_empty())
3400                {
3401                    let store_in_db_trie = !tree_mask.is_empty() || !hash_mask.is_empty();
3402                    if store_in_db_trie {
3403                        // Store in DB trie if there are either any children that are stored in
3404                        // the DB trie, or any children represent hashed values
3405                        hashes.reverse();
3406                        let branch_node = BranchNodeCompact::new(
3407                            *state_mask,
3408                            tree_mask,
3409                            hash_mask,
3410                            hashes,
3411                            hash.filter(|_| path.is_empty()),
3412                        );
3413                        update_actions
3414                            .push(SparseTrieUpdatesAction::InsertUpdated(path, branch_node));
3415                    } else {
3416                        // New tree and hash masks are empty - check previous state
3417                        let prev_had_masks = path_masks()
3418                            .is_some_and(|m| !m.tree_mask.is_empty() || !m.hash_mask.is_empty());
3419                        if prev_had_masks {
3420                            // Previously had masks, now empty - mark as removed
3421                            update_actions.push(SparseTrieUpdatesAction::InsertRemoved(path));
3422                        } else {
3423                            // Previously empty too - just remove the update
3424                            update_actions.push(SparseTrieUpdatesAction::RemoveUpdated(path));
3425                        }
3426                    }
3427
3428                    store_in_db_trie
3429                } else {
3430                    false
3431                };
3432                *store_in_db_trie = Some(store_in_db_trie_value);
3433
3434                (
3435                    rlp_node,
3436                    SparseNodeType::Branch { store_in_db_trie: Some(store_in_db_trie_value) },
3437                )
3438            }
3439        };
3440
3441        trace!(
3442            target: "trie::parallel_sparse",
3443            ?path,
3444            ?node_type,
3445            ?rlp_node,
3446            "Adding node to RLP node stack"
3447        );
3448        self.buffers.rlp_node_stack.push(RlpNodeStackItem { path, rlp_node, node_type });
3449    }
3450
3451    /// Clears the subtrie, keeping the data structures allocated.
3452    fn clear(&mut self) {
3453        self.values.clear();
3454        self.buffers.clear();
3455    }
3456}
3457
3458/// Represents the outcome of processing a node during leaf insertion
3459#[derive(Clone, Debug, PartialEq, Eq, Default)]
3460pub enum LeafUpdateStep {
3461    /// Continue traversing to the next node
3462    Continue {
3463        /// The next node path to process
3464        next_node: Nibbles,
3465    },
3466    /// Update is complete with nodes inserted
3467    Complete {
3468        /// The node paths that were inserted during this step
3469        inserted_nodes: Vec<Nibbles>,
3470        /// Path to a node which may need to be revealed
3471        reveal_path: Option<Nibbles>,
3472    },
3473    /// The node was not found
3474    #[default]
3475    NodeNotFound,
3476}
3477
3478impl LeafUpdateStep {
3479    /// Creates a step to continue with the next node
3480    pub const fn continue_with(next_node: Nibbles) -> Self {
3481        Self::Continue { next_node }
3482    }
3483
3484    /// Creates a step indicating completion with inserted nodes
3485    pub const fn complete_with_insertions(
3486        inserted_nodes: Vec<Nibbles>,
3487        reveal_path: Option<Nibbles>,
3488    ) -> Self {
3489        Self::Complete { inserted_nodes, reveal_path }
3490    }
3491}
3492
3493/// Sparse Subtrie Type.
3494///
3495/// Used to determine the type of subtrie a certain path belongs to:
3496/// - Paths in the range `0x..=0xf` belong to the upper subtrie.
3497/// - Paths in the range `0x00..` belong to one of the lower subtries. The index of the lower
3498///   subtrie is determined by the first [`UPPER_TRIE_MAX_DEPTH`] nibbles of the path.
3499///
3500/// There can be at most [`NUM_LOWER_SUBTRIES`] lower subtries.
3501#[derive(Clone, Copy, PartialEq, Eq, Debug)]
3502pub enum SparseSubtrieType {
3503    /// Upper subtrie with paths in the range `0x..=0xf`
3504    Upper,
3505    /// Lower subtrie with paths in the range `0x00..`. Includes the index of the subtrie,
3506    /// according to the path prefix.
3507    Lower(usize),
3508}
3509
3510impl SparseSubtrieType {
3511    /// Returns true if a node at a path of the given length would be placed in the upper subtrie.
3512    ///
3513    /// Nodes with paths shorter than [`UPPER_TRIE_MAX_DEPTH`] nibbles belong to the upper subtrie,
3514    /// while longer paths belong to the lower subtries.
3515    pub const fn path_len_is_upper(len: usize) -> bool {
3516        len < UPPER_TRIE_MAX_DEPTH
3517    }
3518
3519    /// Returns the type of subtrie based on the given path.
3520    pub fn from_path(path: &Nibbles) -> Self {
3521        if Self::path_len_is_upper(path.len()) {
3522            Self::Upper
3523        } else {
3524            Self::Lower(path_subtrie_index_unchecked(path))
3525        }
3526    }
3527
3528    /// Returns the index of the lower subtrie, if it exists.
3529    pub const fn lower_index(&self) -> Option<usize> {
3530        match self {
3531            Self::Upper => None,
3532            Self::Lower(index) => Some(*index),
3533        }
3534    }
3535}
3536
3537impl Ord for SparseSubtrieType {
3538    /// Orders two [`SparseSubtrieType`]s such that `Upper` is less than `Lower(_)`, and `Lower`s
3539    /// are ordered by their index.
3540    fn cmp(&self, other: &Self) -> Ordering {
3541        match (self, other) {
3542            (Self::Upper, Self::Upper) => Ordering::Equal,
3543            (Self::Upper, Self::Lower(_)) => Ordering::Less,
3544            (Self::Lower(_), Self::Upper) => Ordering::Greater,
3545            (Self::Lower(idx_a), Self::Lower(idx_b)) if idx_a == idx_b => Ordering::Equal,
3546            (Self::Lower(idx_a), Self::Lower(idx_b)) => idx_a.cmp(idx_b),
3547        }
3548    }
3549}
3550
3551impl PartialOrd for SparseSubtrieType {
3552    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3553        Some(self.cmp(other))
3554    }
3555}
3556
3557/// Collection of reusable buffers for calculating subtrie hashes.
3558///
3559/// These buffers reduce allocations when computing RLP representations during trie updates.
3560#[derive(Clone, PartialEq, Eq, Debug, Default)]
3561pub struct SparseSubtrieBuffers {
3562    /// Stack of RLP node paths
3563    path_stack: Vec<RlpNodePathStackItem>,
3564    /// Stack of RLP nodes
3565    rlp_node_stack: Vec<RlpNodeStackItem>,
3566    /// Reusable branch child path
3567    branch_child_buf: Vec<Nibbles>,
3568    /// Reusable branch value stack
3569    branch_value_stack_buf: Vec<RlpNode>,
3570    /// Reusable RLP buffer
3571    rlp_buf: Vec<u8>,
3572}
3573
3574impl SparseSubtrieBuffers {
3575    /// Clears all buffers.
3576    fn clear(&mut self) {
3577        self.path_stack.clear();
3578        self.rlp_node_stack.clear();
3579        self.branch_child_buf.clear();
3580        self.branch_value_stack_buf.clear();
3581        self.rlp_buf.clear();
3582    }
3583
3584    /// Returns a heuristic for the in-memory size of these buffers in bytes.
3585    const fn memory_size(&self) -> usize {
3586        let mut size = core::mem::size_of::<Self>();
3587
3588        size += self.path_stack.capacity() * core::mem::size_of::<RlpNodePathStackItem>();
3589        size += self.rlp_node_stack.capacity() * core::mem::size_of::<RlpNodeStackItem>();
3590        size += self.branch_child_buf.capacity() * core::mem::size_of::<Nibbles>();
3591        size += self.branch_value_stack_buf.capacity() * core::mem::size_of::<RlpNode>();
3592        size += self.rlp_buf.capacity();
3593
3594        size
3595    }
3596}
3597
3598/// RLP node path stack item.
3599#[derive(Clone, PartialEq, Eq, Debug)]
3600pub struct RlpNodePathStackItem {
3601    /// Path to the node.
3602    pub path: Nibbles,
3603    /// Whether the path is in the prefix set. If [`None`], then unknown yet.
3604    pub is_in_prefix_set: Option<bool>,
3605}
3606
3607/// Changed subtrie.
3608#[derive(Debug)]
3609struct ChangedSubtrie {
3610    /// Lower subtrie index in the range [0, [`NUM_LOWER_SUBTRIES`]).
3611    index: usize,
3612    /// Changed subtrie
3613    subtrie: Box<SparseSubtrie>,
3614    /// Prefix set of keys that belong to the subtrie.
3615    prefix_set: PrefixSet,
3616    /// Reusable buffer for collecting [`SparseTrieUpdatesAction`]s during computations. Will be
3617    /// None if update retention is disabled.
3618    update_actions_buf: Option<Vec<SparseTrieUpdatesAction>>,
3619}
3620
3621/// Convert first [`UPPER_TRIE_MAX_DEPTH`] nibbles of the path into a lower subtrie index in the
3622/// range [0, [`NUM_LOWER_SUBTRIES`]).
3623///
3624/// # Panics
3625///
3626/// If the path is shorter than [`UPPER_TRIE_MAX_DEPTH`] nibbles.
3627fn path_subtrie_index_unchecked(path: &Nibbles) -> usize {
3628    debug_assert_eq!(UPPER_TRIE_MAX_DEPTH, 2);
3629    let idx = path.get_byte_unchecked(0) as usize;
3630    // SAFETY: always true.
3631    unsafe { core::hint::assert_unchecked(idx < NUM_LOWER_SUBTRIES) };
3632    idx
3633}
3634
3635/// Checks if `path` is a strict descendant of any root in a sorted slice.
3636///
3637/// Uses binary search to find the candidate root that could be an ancestor.
3638/// Returns `true` if `path` starts with a root and is longer (strict descendant).
3639fn is_strict_descendant_in(roots: &[(Nibbles, B256)], path: &Nibbles) -> bool {
3640    if roots.is_empty() {
3641        return false;
3642    }
3643    debug_assert!(roots.windows(2).all(|w| w[0].0 <= w[1].0), "roots must be sorted by path");
3644    let idx = roots.partition_point(|(root, _)| root <= path);
3645    if idx > 0 {
3646        let candidate = &roots[idx - 1].0;
3647        if path.starts_with(candidate) && path.len() > candidate.len() {
3648            return true;
3649        }
3650    }
3651    false
3652}
3653
3654/// Checks if `path` starts with any root in a sorted slice (inclusive).
3655///
3656/// Uses binary search to find the candidate root that could be a prefix.
3657/// Returns `true` if `path` starts with a root (including exact match).
3658fn starts_with_pruned_in(roots: &[(Nibbles, B256)], path: &Nibbles) -> bool {
3659    if roots.is_empty() {
3660        return false;
3661    }
3662    debug_assert!(roots.windows(2).all(|w| w[0].0 <= w[1].0), "roots must be sorted by path");
3663    let idx = roots.partition_point(|(root, _)| root <= path);
3664    if idx > 0 {
3665        let candidate = &roots[idx - 1].0;
3666        if path.starts_with(candidate) {
3667            return true;
3668        }
3669    }
3670    false
3671}
3672
3673/// Used by lower subtries to communicate updates to the top-level [`SparseTrieUpdates`] set.
3674#[derive(Clone, Debug, Eq, PartialEq)]
3675enum SparseTrieUpdatesAction {
3676    /// Remove the path from the `updated_nodes`, if it was present, and add it to `removed_nodes`.
3677    InsertRemoved(Nibbles),
3678    /// Remove the path from the `updated_nodes`, if it was present, leaving `removed_nodes`
3679    /// unaffected.
3680    RemoveUpdated(Nibbles),
3681    /// Insert the branch node into `updated_nodes`.
3682    InsertUpdated(Nibbles, BranchNodeCompact),
3683}
3684
3685#[cfg(test)]
3686mod tests {
3687    use super::{
3688        path_subtrie_index_unchecked, LowerSparseSubtrie, ParallelSparseTrie, SparseSubtrie,
3689        SparseSubtrieType,
3690    };
3691    use crate::{
3692        parallel::ChangedSubtrie,
3693        provider::{DefaultTrieNodeProvider, RevealedNode, TrieNodeProvider},
3694        LeafLookup, LeafLookupError, SparseNode, SparseTrie, SparseTrieUpdates,
3695    };
3696    use alloy_primitives::{
3697        b256, hex,
3698        map::{B256Set, DefaultHashBuilder, HashMap},
3699        B256, U256,
3700    };
3701    use alloy_rlp::{Decodable, Encodable};
3702    use alloy_trie::{BranchNodeCompact, Nibbles};
3703    use assert_matches::assert_matches;
3704    use itertools::Itertools;
3705    use proptest::{prelude::*, sample::SizeRange};
3706    use proptest_arbitrary_interop::arb;
3707    use reth_execution_errors::{SparseTrieError, SparseTrieErrorKind};
3708    use reth_primitives_traits::Account;
3709    use reth_provider::{test_utils::create_test_provider_factory, TrieWriter};
3710    use reth_trie::{
3711        hashed_cursor::{noop::NoopHashedCursor, HashedPostStateCursor},
3712        node_iter::{TrieElement, TrieNodeIter},
3713        trie_cursor::{noop::NoopAccountTrieCursor, TrieCursor, TrieCursorFactory},
3714        walker::TrieWalker,
3715        HashedPostState,
3716    };
3717    use reth_trie_common::{
3718        prefix_set::PrefixSetMut,
3719        proof::{ProofNodes, ProofRetainer},
3720        updates::TrieUpdates,
3721        BranchNode, BranchNodeMasks, BranchNodeMasksMap, ExtensionNode, HashBuilder, LeafNode,
3722        ProofTrieNode, RlpNode, TrieMask, TrieNode, EMPTY_ROOT_HASH,
3723    };
3724    use reth_trie_db::DatabaseTrieCursorFactory;
3725    use std::collections::{BTreeMap, BTreeSet};
3726
3727    /// Pad nibbles to the length of a B256 hash with zeros on the right.
3728    fn pad_nibbles_right(mut nibbles: Nibbles) -> Nibbles {
3729        nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![
3730            0;
3731            B256::len_bytes() * 2 - nibbles.len()
3732        ]));
3733        nibbles
3734    }
3735
3736    /// Mock trie node provider for testing that allows pre-setting nodes at specific paths.
3737    ///
3738    /// This provider can be used in tests to simulate trie nodes that need to be revealed
3739    /// during trie operations, particularly when collapsing branch nodes during leaf removal.
3740    #[derive(Debug, Clone)]
3741    struct MockTrieNodeProvider {
3742        /// Mapping from path to revealed node data
3743        nodes: HashMap<Nibbles, RevealedNode, DefaultHashBuilder>,
3744    }
3745
3746    impl MockTrieNodeProvider {
3747        /// Creates a new empty mock provider
3748        fn new() -> Self {
3749            Self { nodes: HashMap::default() }
3750        }
3751
3752        /// Adds a revealed node at the specified path
3753        fn add_revealed_node(&mut self, path: Nibbles, node: RevealedNode) {
3754            self.nodes.insert(path, node);
3755        }
3756    }
3757
3758    impl TrieNodeProvider for MockTrieNodeProvider {
3759        fn trie_node(&self, path: &Nibbles) -> Result<Option<RevealedNode>, SparseTrieError> {
3760            Ok(self.nodes.get(path).cloned())
3761        }
3762    }
3763
3764    fn create_account(nonce: u64) -> Account {
3765        Account { nonce, ..Default::default() }
3766    }
3767
3768    fn large_account_value() -> Vec<u8> {
3769        let account = Account {
3770            nonce: 0x123456789abcdef,
3771            balance: U256::from(0x123456789abcdef0123456789abcdef_u128),
3772            ..Default::default()
3773        };
3774        let mut buf = Vec::new();
3775        account.into_trie_account(EMPTY_ROOT_HASH).encode(&mut buf);
3776        buf
3777    }
3778
3779    fn encode_account_value(nonce: u64) -> Vec<u8> {
3780        let account = Account { nonce, ..Default::default() };
3781        let trie_account = account.into_trie_account(EMPTY_ROOT_HASH);
3782        let mut buf = Vec::new();
3783        trie_account.encode(&mut buf);
3784        buf
3785    }
3786
3787    /// Test context that provides helper methods for trie testing
3788    #[derive(Default)]
3789    struct ParallelSparseTrieTestContext;
3790
3791    impl ParallelSparseTrieTestContext {
3792        /// Assert that a lower subtrie exists at the given path
3793        fn assert_subtrie_exists(&self, trie: &ParallelSparseTrie, path: &Nibbles) {
3794            let idx = path_subtrie_index_unchecked(path);
3795            assert!(
3796                trie.lower_subtries[idx].as_revealed_ref().is_some(),
3797                "Expected lower subtrie at path {path:?} to exist",
3798            );
3799        }
3800
3801        /// Get a lower subtrie, panicking if it doesn't exist
3802        fn get_subtrie<'a>(
3803            &self,
3804            trie: &'a ParallelSparseTrie,
3805            path: &Nibbles,
3806        ) -> &'a SparseSubtrie {
3807            let idx = path_subtrie_index_unchecked(path);
3808            trie.lower_subtries[idx]
3809                .as_revealed_ref()
3810                .unwrap_or_else(|| panic!("Lower subtrie at path {path:?} should exist"))
3811        }
3812
3813        /// Assert that a lower subtrie has a specific path field value
3814        fn assert_subtrie_path(
3815            &self,
3816            trie: &ParallelSparseTrie,
3817            subtrie_prefix: impl AsRef<[u8]>,
3818            expected_path: impl AsRef<[u8]>,
3819        ) {
3820            let subtrie_prefix = Nibbles::from_nibbles(subtrie_prefix);
3821            let expected_path = Nibbles::from_nibbles(expected_path);
3822            let idx = path_subtrie_index_unchecked(&subtrie_prefix);
3823
3824            let subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap_or_else(|| {
3825                panic!("Lower subtrie at prefix {subtrie_prefix:?} should exist")
3826            });
3827
3828            assert_eq!(
3829                subtrie.path, expected_path,
3830                "Subtrie at prefix {subtrie_prefix:?} should have path {expected_path:?}, but has {:?}",
3831                subtrie.path
3832            );
3833        }
3834
3835        /// Create test leaves with consecutive account values
3836        fn create_test_leaves(&self, paths: &[&[u8]]) -> Vec<(Nibbles, Vec<u8>)> {
3837            paths
3838                .iter()
3839                .enumerate()
3840                .map(|(i, path)| (Nibbles::from_nibbles(path), encode_account_value(i as u64 + 1)))
3841                .collect()
3842        }
3843
3844        /// Create a single test leaf with the given path and value nonce
3845        fn create_test_leaf(&self, path: impl AsRef<[u8]>, value_nonce: u64) -> (Nibbles, Vec<u8>) {
3846            (Nibbles::from_nibbles(path), encode_account_value(value_nonce))
3847        }
3848
3849        /// Update multiple leaves in the trie
3850        fn update_leaves(
3851            &self,
3852            trie: &mut ParallelSparseTrie,
3853            leaves: impl IntoIterator<Item = (Nibbles, Vec<u8>)>,
3854        ) {
3855            for (path, value) in leaves {
3856                trie.update_leaf(path, value, DefaultTrieNodeProvider).unwrap();
3857            }
3858        }
3859
3860        /// Create an assertion builder for a subtrie
3861        fn assert_subtrie<'a>(
3862            &self,
3863            trie: &'a ParallelSparseTrie,
3864            path: Nibbles,
3865        ) -> SubtrieAssertion<'a> {
3866            self.assert_subtrie_exists(trie, &path);
3867            let subtrie = self.get_subtrie(trie, &path);
3868            SubtrieAssertion::new(subtrie)
3869        }
3870
3871        /// Create an assertion builder for the upper subtrie
3872        fn assert_upper_subtrie<'a>(&self, trie: &'a ParallelSparseTrie) -> SubtrieAssertion<'a> {
3873            SubtrieAssertion::new(&trie.upper_subtrie)
3874        }
3875
3876        /// Assert the root, trie updates, and nodes against the hash builder output.
3877        fn assert_with_hash_builder(
3878            &self,
3879            trie: &mut ParallelSparseTrie,
3880            hash_builder_root: B256,
3881            hash_builder_updates: TrieUpdates,
3882            hash_builder_proof_nodes: ProofNodes,
3883        ) {
3884            assert_eq!(trie.root(), hash_builder_root);
3885            pretty_assertions::assert_eq!(
3886                BTreeMap::from_iter(trie.updates_ref().updated_nodes.clone()),
3887                BTreeMap::from_iter(hash_builder_updates.account_nodes)
3888            );
3889            assert_eq_parallel_sparse_trie_proof_nodes(trie, hash_builder_proof_nodes);
3890        }
3891    }
3892
3893    /// Assertion builder for subtrie structure
3894    struct SubtrieAssertion<'a> {
3895        subtrie: &'a SparseSubtrie,
3896    }
3897
3898    impl<'a> SubtrieAssertion<'a> {
3899        fn new(subtrie: &'a SparseSubtrie) -> Self {
3900            Self { subtrie }
3901        }
3902
3903        fn has_branch(self, path: &Nibbles, expected_mask_bits: &[u8]) -> Self {
3904            match self.subtrie.nodes.get(path) {
3905                Some(SparseNode::Branch { state_mask, .. }) => {
3906                    for bit in expected_mask_bits {
3907                        assert!(
3908                            state_mask.is_bit_set(*bit),
3909                            "Expected branch at {path:?} to have bit {bit} set, instead mask is: {state_mask:?}",
3910                        );
3911                    }
3912                }
3913                node => panic!("Expected branch node at {path:?}, found {node:?}"),
3914            }
3915            self
3916        }
3917
3918        fn has_leaf(self, path: &Nibbles, expected_key: &Nibbles) -> Self {
3919            match self.subtrie.nodes.get(path) {
3920                Some(SparseNode::Leaf { key, .. }) => {
3921                    assert_eq!(
3922                        *key, *expected_key,
3923                        "Expected leaf at {path:?} to have key {expected_key:?}, found {key:?}",
3924                    );
3925                }
3926                node => panic!("Expected leaf node at {path:?}, found {node:?}"),
3927            }
3928            self
3929        }
3930
3931        fn has_extension(self, path: &Nibbles, expected_key: &Nibbles) -> Self {
3932            match self.subtrie.nodes.get(path) {
3933                Some(SparseNode::Extension { key, .. }) => {
3934                    assert_eq!(
3935                        *key, *expected_key,
3936                        "Expected extension at {path:?} to have key {expected_key:?}, found {key:?}",
3937                    );
3938                }
3939                node => panic!("Expected extension node at {path:?}, found {node:?}"),
3940            }
3941            self
3942        }
3943
3944        fn has_hash(self, path: &Nibbles, expected_hash: &B256) -> Self {
3945            match self.subtrie.nodes.get(path) {
3946                Some(SparseNode::Hash(hash)) => {
3947                    assert_eq!(
3948                        *hash, *expected_hash,
3949                        "Expected hash at {path:?} to be {expected_hash:?}, found {hash:?}",
3950                    );
3951                }
3952                node => panic!("Expected hash node at {path:?}, found {node:?}"),
3953            }
3954            self
3955        }
3956
3957        fn has_value(self, path: &Nibbles, expected_value: &[u8]) -> Self {
3958            let actual = self.subtrie.inner.values.get(path);
3959            assert_eq!(
3960                actual.map(|v| v.as_slice()),
3961                Some(expected_value),
3962                "Expected value at {path:?} to be {expected_value:?}, found {actual:?}",
3963            );
3964            self
3965        }
3966
3967        fn has_no_value(self, path: &Nibbles) -> Self {
3968            let actual = self.subtrie.inner.values.get(path);
3969            assert!(actual.is_none(), "Expected no value at {path:?}, but found {actual:?}");
3970            self
3971        }
3972    }
3973
3974    fn create_leaf_node(key: impl AsRef<[u8]>, value_nonce: u64) -> TrieNode {
3975        TrieNode::Leaf(LeafNode::new(Nibbles::from_nibbles(key), encode_account_value(value_nonce)))
3976    }
3977
3978    fn create_extension_node(key: impl AsRef<[u8]>, child_hash: B256) -> TrieNode {
3979        TrieNode::Extension(ExtensionNode::new(
3980            Nibbles::from_nibbles(key),
3981            RlpNode::word_rlp(&child_hash),
3982        ))
3983    }
3984
3985    fn create_branch_node_with_children(
3986        children_indices: &[u8],
3987        child_hashes: impl IntoIterator<Item = RlpNode>,
3988    ) -> TrieNode {
3989        let mut stack = Vec::new();
3990        let mut state_mask = TrieMask::default();
3991
3992        for (&idx, hash) in children_indices.iter().zip(child_hashes.into_iter()) {
3993            state_mask.set_bit(idx);
3994            stack.push(hash);
3995        }
3996
3997        TrieNode::Branch(BranchNode::new(stack, state_mask))
3998    }
3999
4000    /// Calculate the state root by feeding the provided state to the hash builder and retaining the
4001    /// proofs for the provided targets.
4002    ///
4003    /// Returns the state root and the retained proof nodes.
4004    fn run_hash_builder(
4005        state: impl IntoIterator<Item = (Nibbles, Account)> + Clone,
4006        trie_cursor: impl TrieCursor,
4007        destroyed_accounts: B256Set,
4008        proof_targets: impl IntoIterator<Item = Nibbles>,
4009    ) -> (B256, TrieUpdates, ProofNodes, HashMap<Nibbles, TrieMask>, HashMap<Nibbles, TrieMask>)
4010    {
4011        let mut account_rlp = Vec::new();
4012
4013        let mut hash_builder = HashBuilder::default()
4014            .with_updates(true)
4015            .with_proof_retainer(ProofRetainer::from_iter(proof_targets));
4016
4017        let mut prefix_set = PrefixSetMut::default();
4018        prefix_set.extend_keys(state.clone().into_iter().map(|(nibbles, _)| nibbles));
4019        prefix_set.extend_keys(destroyed_accounts.iter().map(Nibbles::unpack));
4020        let walker = TrieWalker::<_>::state_trie(trie_cursor, prefix_set.freeze())
4021            .with_deletions_retained(true);
4022        let hashed_post_state = HashedPostState::default()
4023            .with_accounts(state.into_iter().map(|(nibbles, account)| {
4024                (nibbles.pack().into_inner().unwrap().into(), Some(account))
4025            }))
4026            .into_sorted();
4027        let mut node_iter = TrieNodeIter::state_trie(
4028            walker,
4029            HashedPostStateCursor::new_account(
4030                NoopHashedCursor::<Account>::default(),
4031                &hashed_post_state,
4032            ),
4033        );
4034
4035        while let Some(node) = node_iter.try_next().unwrap() {
4036            match node {
4037                TrieElement::Branch(branch) => {
4038                    hash_builder.add_branch(branch.key, branch.value, branch.children_are_in_trie);
4039                }
4040                TrieElement::Leaf(key, account) => {
4041                    let account = account.into_trie_account(EMPTY_ROOT_HASH);
4042                    account.encode(&mut account_rlp);
4043
4044                    hash_builder.add_leaf(Nibbles::unpack(key), &account_rlp);
4045                    account_rlp.clear();
4046                }
4047            }
4048        }
4049        let root = hash_builder.root();
4050        let proof_nodes = hash_builder.take_proof_nodes();
4051        let branch_node_hash_masks = hash_builder
4052            .updated_branch_nodes
4053            .clone()
4054            .unwrap_or_default()
4055            .iter()
4056            .map(|(path, node)| (*path, node.hash_mask))
4057            .collect();
4058        let branch_node_tree_masks = hash_builder
4059            .updated_branch_nodes
4060            .clone()
4061            .unwrap_or_default()
4062            .iter()
4063            .map(|(path, node)| (*path, node.tree_mask))
4064            .collect();
4065
4066        let mut trie_updates = TrieUpdates::default();
4067        let removed_keys = node_iter.walker.take_removed_keys();
4068        trie_updates.finalize(hash_builder, removed_keys, destroyed_accounts);
4069
4070        (root, trie_updates, proof_nodes, branch_node_hash_masks, branch_node_tree_masks)
4071    }
4072
4073    /// Returns a `ParallelSparseTrie` pre-loaded with the given nodes, as well as leaf values
4074    /// inferred from any provided leaf nodes.
4075    fn new_test_trie<Nodes>(nodes: Nodes) -> ParallelSparseTrie
4076    where
4077        Nodes: Iterator<Item = (Nibbles, SparseNode)>,
4078    {
4079        let mut trie = ParallelSparseTrie::default().with_updates(true);
4080
4081        for (path, node) in nodes {
4082            let subtrie = trie.subtrie_for_path_mut(&path);
4083            if let SparseNode::Leaf { key, .. } = &node {
4084                let mut full_key = path;
4085                full_key.extend(key);
4086                subtrie.inner.values.insert(full_key, "LEAF VALUE".into());
4087            }
4088            subtrie.nodes.insert(path, node);
4089        }
4090        trie
4091    }
4092
4093    fn parallel_sparse_trie_nodes(
4094        sparse_trie: &ParallelSparseTrie,
4095    ) -> impl IntoIterator<Item = (&Nibbles, &SparseNode)> {
4096        let lower_sparse_nodes = sparse_trie
4097            .lower_subtries
4098            .iter()
4099            .filter_map(|subtrie| subtrie.as_revealed_ref())
4100            .flat_map(|subtrie| subtrie.nodes.iter());
4101
4102        let upper_sparse_nodes = sparse_trie.upper_subtrie.nodes.iter();
4103
4104        lower_sparse_nodes.chain(upper_sparse_nodes).sorted_by_key(|(path, _)| *path)
4105    }
4106
4107    /// Assert that the parallel sparse trie nodes and the proof nodes from the hash builder are
4108    /// equal.
4109    fn assert_eq_parallel_sparse_trie_proof_nodes(
4110        sparse_trie: &ParallelSparseTrie,
4111        proof_nodes: ProofNodes,
4112    ) {
4113        let proof_nodes = proof_nodes
4114            .into_nodes_sorted()
4115            .into_iter()
4116            .map(|(path, node)| (path, TrieNode::decode(&mut node.as_ref()).unwrap()));
4117
4118        let all_sparse_nodes = parallel_sparse_trie_nodes(sparse_trie);
4119
4120        for ((proof_node_path, proof_node), (sparse_node_path, sparse_node)) in
4121            proof_nodes.zip(all_sparse_nodes)
4122        {
4123            assert_eq!(&proof_node_path, sparse_node_path);
4124
4125            let equals = match (&proof_node, &sparse_node) {
4126                // Both nodes are empty
4127                (TrieNode::EmptyRoot, SparseNode::Empty) => true,
4128                // Both nodes are branches and have the same state mask
4129                (
4130                    TrieNode::Branch(BranchNode { state_mask: proof_state_mask, .. }),
4131                    SparseNode::Branch { state_mask: sparse_state_mask, .. },
4132                ) => proof_state_mask == sparse_state_mask,
4133                // Both nodes are extensions and have the same key
4134                (
4135                    TrieNode::Extension(ExtensionNode { key: proof_key, .. }),
4136                    SparseNode::Extension { key: sparse_key, .. },
4137                ) |
4138                // Both nodes are leaves and have the same key
4139                (
4140                    TrieNode::Leaf(LeafNode { key: proof_key, .. }),
4141                    SparseNode::Leaf { key: sparse_key, .. },
4142                ) => proof_key == sparse_key,
4143                // Empty and hash nodes are specific to the sparse trie, skip them
4144                (_, SparseNode::Empty | SparseNode::Hash(_)) => continue,
4145                _ => false,
4146            };
4147            assert!(
4148                equals,
4149                "path: {proof_node_path:?}\nproof node: {proof_node:?}\nsparse node: {sparse_node:?}"
4150            );
4151        }
4152    }
4153
4154    #[test]
4155    fn test_get_changed_subtries_empty() {
4156        let mut trie = ParallelSparseTrie::default();
4157        let mut prefix_set = PrefixSetMut::from([Nibbles::default()]).freeze();
4158
4159        let (subtries, unchanged_prefix_set) = trie.take_changed_lower_subtries(&mut prefix_set);
4160        assert!(subtries.is_empty());
4161        assert_eq!(unchanged_prefix_set, PrefixSetMut::from(prefix_set.iter().copied()));
4162    }
4163
4164    #[test]
4165    fn test_get_changed_subtries() {
4166        // Create a trie with three subtries
4167        let mut trie = ParallelSparseTrie::default();
4168        let subtrie_1 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
4169        let subtrie_1_index = path_subtrie_index_unchecked(&subtrie_1.path);
4170        let subtrie_2 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0])));
4171        let subtrie_2_index = path_subtrie_index_unchecked(&subtrie_2.path);
4172        let subtrie_3 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0])));
4173        let subtrie_3_index = path_subtrie_index_unchecked(&subtrie_3.path);
4174
4175        // Add subtries at specific positions
4176        trie.lower_subtries[subtrie_1_index] = LowerSparseSubtrie::Revealed(subtrie_1.clone());
4177        trie.lower_subtries[subtrie_2_index] = LowerSparseSubtrie::Revealed(subtrie_2.clone());
4178        trie.lower_subtries[subtrie_3_index] = LowerSparseSubtrie::Revealed(subtrie_3);
4179
4180        let unchanged_prefix_set = PrefixSetMut::from([
4181            Nibbles::from_nibbles([0x0]),
4182            Nibbles::from_nibbles([0x2, 0x0, 0x0]),
4183        ]);
4184        // Create a prefix set with the keys that match only the second subtrie
4185        let mut prefix_set = PrefixSetMut::from([
4186            // Match second subtrie
4187            Nibbles::from_nibbles([0x1, 0x0, 0x0]),
4188            Nibbles::from_nibbles([0x1, 0x0, 0x1, 0x0]),
4189        ]);
4190        prefix_set.extend(unchanged_prefix_set);
4191        let mut prefix_set = prefix_set.freeze();
4192
4193        // Second subtrie should be removed and returned
4194        let (subtries, unchanged_prefix_set) = trie.take_changed_lower_subtries(&mut prefix_set);
4195        assert_eq!(
4196            subtries
4197                .into_iter()
4198                .map(|ChangedSubtrie { index, subtrie, prefix_set, .. }| {
4199                    (index, subtrie, prefix_set.iter().copied().collect::<Vec<_>>())
4200                })
4201                .collect::<Vec<_>>(),
4202            vec![(
4203                subtrie_2_index,
4204                subtrie_2,
4205                vec![
4206                    Nibbles::from_nibbles([0x1, 0x0, 0x0]),
4207                    Nibbles::from_nibbles([0x1, 0x0, 0x1, 0x0])
4208                ]
4209            )]
4210        );
4211        assert_eq!(unchanged_prefix_set, unchanged_prefix_set);
4212        assert!(trie.lower_subtries[subtrie_2_index].as_revealed_ref().is_none());
4213
4214        // First subtrie should remain unchanged
4215        assert_eq!(trie.lower_subtries[subtrie_1_index], LowerSparseSubtrie::Revealed(subtrie_1));
4216    }
4217
4218    #[test]
4219    fn test_get_changed_subtries_all() {
4220        // Create a trie with three subtries
4221        let mut trie = ParallelSparseTrie::default();
4222        let subtrie_1 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
4223        let subtrie_1_index = path_subtrie_index_unchecked(&subtrie_1.path);
4224        let subtrie_2 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0])));
4225        let subtrie_2_index = path_subtrie_index_unchecked(&subtrie_2.path);
4226        let subtrie_3 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0])));
4227        let subtrie_3_index = path_subtrie_index_unchecked(&subtrie_3.path);
4228
4229        // Add subtries at specific positions
4230        trie.lower_subtries[subtrie_1_index] = LowerSparseSubtrie::Revealed(subtrie_1.clone());
4231        trie.lower_subtries[subtrie_2_index] = LowerSparseSubtrie::Revealed(subtrie_2.clone());
4232        trie.lower_subtries[subtrie_3_index] = LowerSparseSubtrie::Revealed(subtrie_3.clone());
4233
4234        // Create a prefix set that matches any key
4235        let mut prefix_set = PrefixSetMut::all().freeze();
4236
4237        // All subtries should be removed and returned
4238        let (subtries, unchanged_prefix_set) = trie.take_changed_lower_subtries(&mut prefix_set);
4239        assert_eq!(
4240            subtries
4241                .into_iter()
4242                .map(|ChangedSubtrie { index, subtrie, prefix_set, .. }| {
4243                    (index, subtrie, prefix_set.all())
4244                })
4245                .collect::<Vec<_>>(),
4246            vec![
4247                (subtrie_1_index, subtrie_1, true),
4248                (subtrie_2_index, subtrie_2, true),
4249                (subtrie_3_index, subtrie_3, true)
4250            ]
4251        );
4252        assert_eq!(unchanged_prefix_set, PrefixSetMut::all());
4253
4254        assert!(trie.lower_subtries.iter().all(|subtrie| subtrie.as_revealed_ref().is_none()));
4255    }
4256
4257    #[test]
4258    fn test_sparse_subtrie_type() {
4259        assert_eq!(SparseSubtrieType::from_path(&Nibbles::new()), SparseSubtrieType::Upper);
4260        assert_eq!(
4261            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0])),
4262            SparseSubtrieType::Upper
4263        );
4264        assert_eq!(
4265            SparseSubtrieType::from_path(&Nibbles::from_nibbles([15])),
4266            SparseSubtrieType::Upper
4267        );
4268        assert_eq!(
4269            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0, 0])),
4270            SparseSubtrieType::Lower(0)
4271        );
4272        assert_eq!(
4273            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0, 0, 0])),
4274            SparseSubtrieType::Lower(0)
4275        );
4276        assert_eq!(
4277            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0, 1])),
4278            SparseSubtrieType::Lower(1)
4279        );
4280        assert_eq!(
4281            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0, 1, 0])),
4282            SparseSubtrieType::Lower(1)
4283        );
4284        assert_eq!(
4285            SparseSubtrieType::from_path(&Nibbles::from_nibbles([0, 15])),
4286            SparseSubtrieType::Lower(15)
4287        );
4288        assert_eq!(
4289            SparseSubtrieType::from_path(&Nibbles::from_nibbles([15, 0])),
4290            SparseSubtrieType::Lower(240)
4291        );
4292        assert_eq!(
4293            SparseSubtrieType::from_path(&Nibbles::from_nibbles([15, 1])),
4294            SparseSubtrieType::Lower(241)
4295        );
4296        assert_eq!(
4297            SparseSubtrieType::from_path(&Nibbles::from_nibbles([15, 15])),
4298            SparseSubtrieType::Lower(255)
4299        );
4300        assert_eq!(
4301            SparseSubtrieType::from_path(&Nibbles::from_nibbles([15, 15, 15])),
4302            SparseSubtrieType::Lower(255)
4303        );
4304    }
4305
4306    #[test]
4307    fn test_reveal_node_leaves() {
4308        // Reveal leaf in the upper trie. A root branch with child 0x1 makes path [0x1]
4309        // reachable for the subsequent reveal_nodes call.
4310        let root_branch =
4311            create_branch_node_with_children(&[0x1], [RlpNode::word_rlp(&B256::repeat_byte(0xAA))]);
4312        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
4313
4314        {
4315            let path = Nibbles::from_nibbles([0x1]);
4316            let node = create_leaf_node([0x2, 0x3], 42);
4317            let masks = None;
4318
4319            trie.reveal_nodes(&mut [ProofTrieNode { path, node, masks }]).unwrap();
4320
4321            assert_matches!(
4322                trie.upper_subtrie.nodes.get(&path),
4323                Some(SparseNode::Leaf { key, hash: Some(_) })
4324                if key == &Nibbles::from_nibbles([0x2, 0x3])
4325            );
4326
4327            let full_path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
4328            assert_eq!(
4329                trie.upper_subtrie.inner.values.get(&full_path),
4330                Some(&encode_account_value(42))
4331            );
4332        }
4333
4334        // Reveal leaf in a lower trie. A separate trie is needed because the structure at
4335        // [0x1] conflicts: the upper trie test placed a leaf there, but reaching [0x1, 0x2]
4336        // requires a branch at [0x1]. A root branch → branch at [0x1] with child 0x2
4337        // makes path [0x1, 0x2] reachable.
4338        let root_branch =
4339            create_branch_node_with_children(&[0x1], [RlpNode::word_rlp(&B256::repeat_byte(0xAA))]);
4340        let branch_at_1 =
4341            create_branch_node_with_children(&[0x2], [RlpNode::word_rlp(&B256::repeat_byte(0xBB))]);
4342        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
4343        trie.reveal_nodes(&mut [ProofTrieNode {
4344            path: Nibbles::from_nibbles([0x1]),
4345            node: branch_at_1,
4346            masks: None,
4347        }])
4348        .unwrap();
4349
4350        {
4351            let path = Nibbles::from_nibbles([0x1, 0x2]);
4352            let node = create_leaf_node([0x3, 0x4], 42);
4353            let masks = None;
4354
4355            trie.reveal_nodes(&mut [ProofTrieNode { path, node, masks }]).unwrap();
4356
4357            // Check that the lower subtrie was created
4358            let idx = path_subtrie_index_unchecked(&path);
4359            assert!(trie.lower_subtries[idx].as_revealed_ref().is_some());
4360
4361            // Check that the lower subtrie's path was correctly set
4362            let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
4363            assert_eq!(lower_subtrie.path, path);
4364
4365            assert_matches!(
4366                lower_subtrie.nodes.get(&path),
4367                Some(SparseNode::Leaf { key, hash: Some(_) })
4368                if key == &Nibbles::from_nibbles([0x3, 0x4])
4369            );
4370        }
4371
4372        // Reveal leaf in a lower trie with a longer path, shouldn't result in the subtrie's root
4373        // path changing.
4374        {
4375            let path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
4376            let node = create_leaf_node([0x4, 0x5], 42);
4377            let masks = None;
4378
4379            trie.reveal_nodes(&mut [ProofTrieNode { path, node, masks }]).unwrap();
4380
4381            // Check that the lower subtrie's path hasn't changed
4382            let idx = path_subtrie_index_unchecked(&path);
4383            let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
4384            assert_eq!(lower_subtrie.path, Nibbles::from_nibbles([0x1, 0x2]));
4385        }
4386    }
4387
4388    #[test]
4389    fn test_reveal_node_extension_all_upper() {
4390        let path = Nibbles::new();
4391        let child_hash = B256::repeat_byte(0xab);
4392        let node = create_extension_node([0x1], child_hash);
4393        let masks = None;
4394        let trie = ParallelSparseTrie::from_root(node, masks, true).unwrap();
4395
4396        assert_matches!(
4397            trie.upper_subtrie.nodes.get(&path),
4398            Some(SparseNode::Extension { key, hash: None, .. })
4399            if key == &Nibbles::from_nibbles([0x1])
4400        );
4401
4402        // Child path should be in upper trie
4403        let child_path = Nibbles::from_nibbles([0x1]);
4404        assert_eq!(trie.upper_subtrie.nodes.get(&child_path), Some(&SparseNode::Hash(child_hash)));
4405    }
4406
4407    #[test]
4408    fn test_reveal_node_extension_cross_level() {
4409        let path = Nibbles::new();
4410        let child_hash = B256::repeat_byte(0xcd);
4411        let node = create_extension_node([0x1, 0x2, 0x3], child_hash);
4412        let masks = None;
4413        let trie = ParallelSparseTrie::from_root(node, masks, true).unwrap();
4414
4415        // Extension node should be in upper trie
4416        assert_matches!(
4417            trie.upper_subtrie.nodes.get(&path),
4418            Some(SparseNode::Extension { key, hash: None, .. })
4419            if key == &Nibbles::from_nibbles([0x1, 0x2, 0x3])
4420        );
4421
4422        // Child path (0x1, 0x2, 0x3) should be in lower trie
4423        let child_path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
4424        let idx = path_subtrie_index_unchecked(&child_path);
4425        assert!(trie.lower_subtries[idx].as_revealed_ref().is_some());
4426
4427        let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
4428        assert_eq!(lower_subtrie.path, child_path);
4429        assert_eq!(lower_subtrie.nodes.get(&child_path), Some(&SparseNode::Hash(child_hash)));
4430    }
4431
4432    #[test]
4433    fn test_reveal_node_extension_cross_level_boundary() {
4434        // Set up root branch with nibble 0x1 so path [0x1] is reachable.
4435        let root_branch =
4436            create_branch_node_with_children(&[0x1], [RlpNode::word_rlp(&B256::repeat_byte(0xAA))]);
4437        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
4438
4439        let path = Nibbles::from_nibbles([0x1]);
4440        let child_hash = B256::repeat_byte(0xcd);
4441        let node = create_extension_node([0x2], child_hash);
4442        let masks = None;
4443
4444        trie.reveal_nodes(&mut [ProofTrieNode { path, node, masks }]).unwrap();
4445
4446        // Extension node should be in upper trie, hash is memoized from the previous Hash node
4447        assert_matches!(
4448            trie.upper_subtrie.nodes.get(&path),
4449            Some(SparseNode::Extension { key, hash: Some(_), .. })
4450            if key == &Nibbles::from_nibbles([0x2])
4451        );
4452
4453        // Child path (0x1, 0x2) should be in lower trie
4454        let child_path = Nibbles::from_nibbles([0x1, 0x2]);
4455        let idx = path_subtrie_index_unchecked(&child_path);
4456        assert!(trie.lower_subtries[idx].as_revealed_ref().is_some());
4457
4458        let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
4459        assert_eq!(lower_subtrie.path, child_path);
4460        assert_eq!(lower_subtrie.nodes.get(&child_path), Some(&SparseNode::Hash(child_hash)));
4461    }
4462
4463    #[test]
4464    fn test_reveal_node_branch_all_upper() {
4465        let path = Nibbles::new();
4466        let child_hashes = [
4467            RlpNode::word_rlp(&B256::repeat_byte(0x11)),
4468            RlpNode::word_rlp(&B256::repeat_byte(0x22)),
4469        ];
4470        let node = create_branch_node_with_children(&[0x0, 0x5], child_hashes.clone());
4471        let masks = None;
4472        let trie = ParallelSparseTrie::from_root(node, masks, true).unwrap();
4473
4474        // Branch node should be in upper trie
4475        assert_matches!(
4476            trie.upper_subtrie.nodes.get(&path),
4477            Some(SparseNode::Branch { state_mask, hash: None, .. })
4478            if *state_mask == 0b0000000000100001.into()
4479        );
4480
4481        // Children should be in upper trie (paths of length 2)
4482        let child_path_0 = Nibbles::from_nibbles([0x0]);
4483        let child_path_5 = Nibbles::from_nibbles([0x5]);
4484        assert_eq!(
4485            trie.upper_subtrie.nodes.get(&child_path_0),
4486            Some(&SparseNode::Hash(child_hashes[0].as_hash().unwrap()))
4487        );
4488        assert_eq!(
4489            trie.upper_subtrie.nodes.get(&child_path_5),
4490            Some(&SparseNode::Hash(child_hashes[1].as_hash().unwrap()))
4491        );
4492    }
4493
4494    #[test]
4495    fn test_reveal_node_branch_cross_level() {
4496        // Set up root branch with nibble 0x1 so path [0x1] is reachable.
4497        let root_branch =
4498            create_branch_node_with_children(&[0x1], [RlpNode::word_rlp(&B256::repeat_byte(0xAA))]);
4499        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
4500
4501        let path = Nibbles::from_nibbles([0x1]); // Exactly 1 nibbles - boundary case
4502        let child_hashes = [
4503            RlpNode::word_rlp(&B256::repeat_byte(0x33)),
4504            RlpNode::word_rlp(&B256::repeat_byte(0x44)),
4505            RlpNode::word_rlp(&B256::repeat_byte(0x55)),
4506        ];
4507        let node = create_branch_node_with_children(&[0x0, 0x7, 0xf], child_hashes.clone());
4508        let masks = None;
4509
4510        trie.reveal_nodes(&mut [ProofTrieNode { path, node, masks }]).unwrap();
4511
4512        // Branch node should be in upper trie, hash is memoized from the previous Hash node
4513        assert_matches!(
4514            trie.upper_subtrie.nodes.get(&path),
4515            Some(SparseNode::Branch { state_mask, hash: Some(_), .. })
4516            if *state_mask == 0b1000000010000001.into()
4517        );
4518
4519        // All children should be in lower tries since they have paths of length 3
4520        let child_paths = [
4521            Nibbles::from_nibbles([0x1, 0x0]),
4522            Nibbles::from_nibbles([0x1, 0x7]),
4523            Nibbles::from_nibbles([0x1, 0xf]),
4524        ];
4525
4526        for (i, child_path) in child_paths.iter().enumerate() {
4527            let idx = path_subtrie_index_unchecked(child_path);
4528            let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
4529            assert_eq!(&lower_subtrie.path, child_path);
4530            assert_eq!(
4531                lower_subtrie.nodes.get(child_path),
4532                Some(&SparseNode::Hash(child_hashes[i].as_hash().unwrap())),
4533            );
4534        }
4535    }
4536
4537    #[test]
4538    fn test_update_subtrie_hashes_prefix_set_matching() {
4539        // Create a trie with a root branch that makes paths [0x0, ...] and [0x3, ...]
4540        // reachable from the upper trie.
4541        let root_branch = create_branch_node_with_children(
4542            &[0x0, 0x3],
4543            [
4544                RlpNode::word_rlp(&B256::repeat_byte(0xAA)),
4545                RlpNode::word_rlp(&B256::repeat_byte(0xBB)),
4546            ],
4547        );
4548        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
4549
4550        // Create leaf paths.
4551        let leaf_1_full_path = Nibbles::from_nibbles([0; 64]);
4552        let leaf_1_path = leaf_1_full_path.slice(..2);
4553        let leaf_1_key = leaf_1_full_path.slice(2..);
4554        let leaf_2_full_path = Nibbles::from_nibbles([vec![0, 1], vec![0; 62]].concat());
4555        let leaf_2_path = leaf_2_full_path.slice(..2);
4556        let leaf_2_key = leaf_2_full_path.slice(2..);
4557        let leaf_3_full_path = Nibbles::from_nibbles([vec![0, 2], vec![0; 62]].concat());
4558        let leaf_1 = create_leaf_node(leaf_1_key.to_vec(), 1);
4559        let leaf_2 = create_leaf_node(leaf_2_key.to_vec(), 2);
4560
4561        // Create branch node at [0x0] with only children 0x0 and 0x1.
4562        // Child 0x2 (leaf_3) will be inserted via update_leaf to create a fresh node
4563        // with hash: None.
4564        let child_hashes = [
4565            RlpNode::word_rlp(&B256::repeat_byte(0x00)),
4566            RlpNode::word_rlp(&B256::repeat_byte(0x11)),
4567        ];
4568        let branch_path = Nibbles::from_nibbles([0x0]);
4569        let branch_node = create_branch_node_with_children(&[0x0, 0x1], child_hashes);
4570
4571        // Reveal the existing nodes
4572        trie.reveal_nodes(&mut [
4573            ProofTrieNode { path: branch_path, node: branch_node, masks: None },
4574            ProofTrieNode { path: leaf_1_path, node: leaf_1, masks: None },
4575            ProofTrieNode { path: leaf_2_path, node: leaf_2, masks: None },
4576        ])
4577        .unwrap();
4578
4579        // Insert leaf_3 via update_leaf. This modifies the branch at [0x0] to add child
4580        // 0x2 and creates a fresh leaf node with hash: None in the lower subtrie.
4581        let provider = MockTrieNodeProvider::new();
4582        trie.update_leaf(leaf_3_full_path, encode_account_value(3), provider).unwrap();
4583
4584        // Calculate subtrie indexes
4585        let subtrie_1_index = SparseSubtrieType::from_path(&leaf_1_path).lower_index().unwrap();
4586        let subtrie_2_index = SparseSubtrieType::from_path(&leaf_2_path).lower_index().unwrap();
4587        let leaf_3_path = leaf_3_full_path.slice(..2);
4588        let subtrie_3_index = SparseSubtrieType::from_path(&leaf_3_path).lower_index().unwrap();
4589
4590        let mut unchanged_prefix_set = PrefixSetMut::from([
4591            Nibbles::from_nibbles([0x0]),
4592            leaf_2_full_path,
4593            Nibbles::from_nibbles([0x3, 0x0, 0x0]),
4594        ]);
4595        // Create a prefix set with the keys that match only the second subtrie
4596        let mut prefix_set = PrefixSetMut::from([
4597            // Match second subtrie
4598            Nibbles::from_nibbles([0x0, 0x1, 0x0]),
4599            Nibbles::from_nibbles([0x0, 0x1, 0x1, 0x0]),
4600        ]);
4601        prefix_set.extend(unchanged_prefix_set.clone());
4602        trie.prefix_set = prefix_set;
4603
4604        // Update subtrie hashes
4605        trie.update_subtrie_hashes();
4606
4607        // We expect that leaf 3 (0x02) should have been added to the prefix set, because it is
4608        // missing a hash and is the root node of a lower subtrie, and therefore would need to have
4609        // that hash calculated by `update_upper_subtrie_hashes`.
4610        unchanged_prefix_set.insert(leaf_3_full_path);
4611
4612        // Check that the prefix set was updated
4613        assert_eq!(
4614            trie.prefix_set.clone().freeze().into_iter().collect::<Vec<_>>(),
4615            unchanged_prefix_set.freeze().into_iter().collect::<Vec<_>>()
4616        );
4617        // Check that subtries were returned back to the array
4618        assert!(trie.lower_subtries[subtrie_1_index].as_revealed_ref().is_some());
4619        assert!(trie.lower_subtries[subtrie_2_index].as_revealed_ref().is_some());
4620        assert!(trie.lower_subtries[subtrie_3_index].as_revealed_ref().is_some());
4621    }
4622
4623    #[test]
4624    fn test_subtrie_update_hashes() {
4625        let mut subtrie = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
4626
4627        // Create leaf nodes with paths 0x0...0, 0x00001...0, 0x0010...0
4628        let leaf_1_full_path = Nibbles::from_nibbles([0; 64]);
4629        let leaf_1_path = leaf_1_full_path.slice(..5);
4630        let leaf_1_key = leaf_1_full_path.slice(5..);
4631        let leaf_2_full_path = Nibbles::from_nibbles([vec![0, 0, 0, 0, 1], vec![0; 59]].concat());
4632        let leaf_2_path = leaf_2_full_path.slice(..5);
4633        let leaf_2_key = leaf_2_full_path.slice(5..);
4634        let leaf_3_full_path = Nibbles::from_nibbles([vec![0, 0, 1], vec![0; 61]].concat());
4635        let leaf_3_path = leaf_3_full_path.slice(..3);
4636        let leaf_3_key = leaf_3_full_path.slice(3..);
4637
4638        let account_1 = create_account(1);
4639        let account_2 = create_account(2);
4640        let account_3 = create_account(3);
4641        let leaf_1 = create_leaf_node(leaf_1_key.to_vec(), account_1.nonce);
4642        let leaf_2 = create_leaf_node(leaf_2_key.to_vec(), account_2.nonce);
4643        let leaf_3 = create_leaf_node(leaf_3_key.to_vec(), account_3.nonce);
4644
4645        // Create bottom branch node
4646        let branch_1_path = Nibbles::from_nibbles([0, 0, 0, 0]);
4647        let branch_1 = create_branch_node_with_children(
4648            &[0, 1],
4649            vec![
4650                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1)),
4651                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2)),
4652            ],
4653        );
4654
4655        // Create an extension node
4656        let extension_path = Nibbles::from_nibbles([0, 0, 0]);
4657        let extension_key = Nibbles::from_nibbles([0]);
4658        let extension = create_extension_node(
4659            extension_key.to_vec(),
4660            RlpNode::from_rlp(&alloy_rlp::encode(&branch_1)).as_hash().unwrap(),
4661        );
4662
4663        // Create top branch node
4664        let branch_2_path = Nibbles::from_nibbles([0, 0]);
4665        let branch_2 = create_branch_node_with_children(
4666            &[0, 1],
4667            vec![
4668                RlpNode::from_rlp(&alloy_rlp::encode(&extension)),
4669                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_3)),
4670            ],
4671        );
4672
4673        // Reveal nodes
4674        subtrie.reveal_node(branch_2_path, &branch_2, None).unwrap();
4675        subtrie.reveal_node(leaf_1_path, &leaf_1, None).unwrap();
4676        subtrie.reveal_node(extension_path, &extension, None).unwrap();
4677        subtrie.reveal_node(branch_1_path, &branch_1, None).unwrap();
4678        subtrie.reveal_node(leaf_2_path, &leaf_2, None).unwrap();
4679        subtrie.reveal_node(leaf_3_path, &leaf_3, None).unwrap();
4680
4681        // Run hash builder for two leaf nodes
4682        let (_, _, proof_nodes, _, _) = run_hash_builder(
4683            [
4684                (leaf_1_full_path, account_1),
4685                (leaf_2_full_path, account_2),
4686                (leaf_3_full_path, account_3),
4687            ],
4688            NoopAccountTrieCursor::default(),
4689            Default::default(),
4690            [
4691                branch_1_path,
4692                extension_path,
4693                branch_2_path,
4694                leaf_1_full_path,
4695                leaf_2_full_path,
4696                leaf_3_full_path,
4697            ],
4698        );
4699
4700        // Update hashes for the subtrie
4701        subtrie.update_hashes(
4702            &mut PrefixSetMut::from([leaf_1_full_path, leaf_2_full_path, leaf_3_full_path])
4703                .freeze(),
4704            &mut None,
4705            &BranchNodeMasksMap::default(),
4706        );
4707
4708        // Compare hashes between hash builder and subtrie
4709        let hash_builder_branch_1_hash =
4710            RlpNode::from_rlp(proof_nodes.get(&branch_1_path).unwrap().as_ref()).as_hash().unwrap();
4711        let subtrie_branch_1_hash = subtrie.nodes.get(&branch_1_path).unwrap().hash().unwrap();
4712        assert_eq!(hash_builder_branch_1_hash, subtrie_branch_1_hash);
4713
4714        let hash_builder_extension_hash =
4715            RlpNode::from_rlp(proof_nodes.get(&extension_path).unwrap().as_ref())
4716                .as_hash()
4717                .unwrap();
4718        let subtrie_extension_hash = subtrie.nodes.get(&extension_path).unwrap().hash().unwrap();
4719        assert_eq!(hash_builder_extension_hash, subtrie_extension_hash);
4720
4721        let hash_builder_branch_2_hash =
4722            RlpNode::from_rlp(proof_nodes.get(&branch_2_path).unwrap().as_ref()).as_hash().unwrap();
4723        let subtrie_branch_2_hash = subtrie.nodes.get(&branch_2_path).unwrap().hash().unwrap();
4724        assert_eq!(hash_builder_branch_2_hash, subtrie_branch_2_hash);
4725
4726        let subtrie_leaf_1_hash = subtrie.nodes.get(&leaf_1_path).unwrap().hash().unwrap();
4727        let hash_builder_leaf_1_hash =
4728            RlpNode::from_rlp(proof_nodes.get(&leaf_1_path).unwrap().as_ref()).as_hash().unwrap();
4729        assert_eq!(hash_builder_leaf_1_hash, subtrie_leaf_1_hash);
4730
4731        let hash_builder_leaf_2_hash =
4732            RlpNode::from_rlp(proof_nodes.get(&leaf_2_path).unwrap().as_ref()).as_hash().unwrap();
4733        let subtrie_leaf_2_hash = subtrie.nodes.get(&leaf_2_path).unwrap().hash().unwrap();
4734        assert_eq!(hash_builder_leaf_2_hash, subtrie_leaf_2_hash);
4735
4736        let hash_builder_leaf_3_hash =
4737            RlpNode::from_rlp(proof_nodes.get(&leaf_3_path).unwrap().as_ref()).as_hash().unwrap();
4738        let subtrie_leaf_3_hash = subtrie.nodes.get(&leaf_3_path).unwrap().hash().unwrap();
4739        assert_eq!(hash_builder_leaf_3_hash, subtrie_leaf_3_hash);
4740    }
4741
4742    #[test]
4743    fn test_remove_leaf_branch_becomes_extension() {
4744        //
4745        // 0x:      Extension (Key = 5)
4746        // 0x5:     └── Branch (Mask = 1001)
4747        // 0x50:        ├── 0 -> Extension (Key = 23)
4748        // 0x5023:      │        └── Branch (Mask = 0101)
4749        // 0x50231:     │            ├── 1 -> Leaf
4750        // 0x50233:     │            └── 3 -> Leaf
4751        // 0x53:        └── 3 -> Leaf (Key = 7)
4752        //
4753        // After removing 0x53, extension+branch+extension become a single extension
4754        //
4755        let mut trie = new_test_trie(
4756            [
4757                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
4758                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(TrieMask::new(0b1001))),
4759                (
4760                    Nibbles::from_nibbles([0x5, 0x0]),
4761                    SparseNode::new_ext(Nibbles::from_nibbles([0x2, 0x3])),
4762                ),
4763                (
4764                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3]),
4765                    SparseNode::new_branch(TrieMask::new(0b0101)),
4766                ),
4767                (
4768                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]),
4769                    SparseNode::new_leaf(Nibbles::new()),
4770                ),
4771                (
4772                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]),
4773                    SparseNode::new_leaf(Nibbles::new()),
4774                ),
4775                (
4776                    Nibbles::from_nibbles([0x5, 0x3]),
4777                    SparseNode::new_leaf(Nibbles::from_nibbles([0x7])),
4778                ),
4779            ]
4780            .into_iter(),
4781        );
4782
4783        let provider = MockTrieNodeProvider::new();
4784
4785        // Remove the leaf with a full path of 0x537
4786        let leaf_full_path = Nibbles::from_nibbles([0x5, 0x3, 0x7]);
4787        trie.remove_leaf(&leaf_full_path, provider).unwrap();
4788
4789        let upper_subtrie = &trie.upper_subtrie;
4790        let lower_subtrie_50 = trie.lower_subtries[0x50].as_revealed_ref().unwrap();
4791
4792        // Check that the `SparseSubtrie` the leaf was removed from was itself removed, as it is now
4793        // empty.
4794        assert_matches!(trie.lower_subtries[0x53].as_revealed_ref(), None);
4795
4796        // Check that the leaf node was removed, and that its parent/grandparent were modified
4797        // appropriately.
4798        assert_matches!(
4799            upper_subtrie.nodes.get(&Nibbles::from_nibbles([])),
4800            Some(SparseNode::Extension{ key, ..})
4801            if key == &Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3])
4802        );
4803        assert_matches!(upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x5])), None);
4804        assert_matches!(lower_subtrie_50.nodes.get(&Nibbles::from_nibbles([0x5, 0x0])), None);
4805        assert_matches!(
4806            lower_subtrie_50.nodes.get(&Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3])),
4807            Some(SparseNode::Branch{ state_mask, .. })
4808            if *state_mask == 0b0101.into()
4809        );
4810    }
4811
4812    #[test]
4813    fn test_remove_leaf_branch_becomes_leaf() {
4814        //
4815        // 0x:      Branch (Mask = 0011)
4816        // 0x0:     ├── 0 -> Leaf (Key = 12)
4817        // 0x1:     └── 1 -> Leaf (Key = 34)
4818        //
4819        // After removing 0x012, branch becomes a leaf
4820        //
4821        let mut trie = new_test_trie(
4822            [
4823                (Nibbles::default(), SparseNode::new_branch(TrieMask::new(0b0011))),
4824                (
4825                    Nibbles::from_nibbles([0x0]),
4826                    SparseNode::new_leaf(Nibbles::from_nibbles([0x1, 0x2])),
4827                ),
4828                (
4829                    Nibbles::from_nibbles([0x1]),
4830                    SparseNode::new_leaf(Nibbles::from_nibbles([0x3, 0x4])),
4831                ),
4832            ]
4833            .into_iter(),
4834        );
4835
4836        // Add the branch node to updated_nodes to simulate it being modified earlier
4837        if let Some(updates) = trie.updates.as_mut() {
4838            updates
4839                .updated_nodes
4840                .insert(Nibbles::default(), BranchNodeCompact::new(0b11, 0, 0, vec![], None));
4841        }
4842
4843        let provider = MockTrieNodeProvider::new();
4844
4845        // Remove the leaf with a full path of 0x012
4846        let leaf_full_path = Nibbles::from_nibbles([0x0, 0x1, 0x2]);
4847        trie.remove_leaf(&leaf_full_path, provider).unwrap();
4848
4849        let upper_subtrie = &trie.upper_subtrie;
4850
4851        // Check that the leaf's value was removed
4852        assert_matches!(upper_subtrie.inner.values.get(&leaf_full_path), None);
4853
4854        // Check that the branch node collapsed into a leaf node with the remaining child's key
4855        assert_matches!(
4856            upper_subtrie.nodes.get(&Nibbles::default()),
4857            Some(SparseNode::Leaf{ key, ..})
4858            if key == &Nibbles::from_nibbles([0x1, 0x3, 0x4])
4859        );
4860
4861        // Check that the remaining child node was removed
4862        assert_matches!(upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x1])), None);
4863        // Check that the removed child node was also removed
4864        assert_matches!(upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x0])), None);
4865
4866        // Check that updates were tracked correctly when branch collapsed
4867        let updates = trie.updates.as_ref().unwrap();
4868
4869        // The branch at root should be marked as removed since it collapsed
4870        assert!(updates.removed_nodes.contains(&Nibbles::default()));
4871
4872        // The branch should no longer be in updated_nodes
4873        assert!(!updates.updated_nodes.contains_key(&Nibbles::default()));
4874    }
4875
4876    #[test]
4877    fn test_remove_leaf_extension_becomes_leaf() {
4878        //
4879        // 0x:      Extension (Key = 5)
4880        // 0x5:     └── Branch (Mask = 0011)
4881        // 0x50:        ├── 0 -> Leaf (Key = 12)
4882        // 0x51:        └── 1 -> Leaf (Key = 34)
4883        //
4884        // After removing 0x5012, extension+branch becomes a leaf
4885        //
4886        let mut trie = new_test_trie(
4887            [
4888                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
4889                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(TrieMask::new(0b0011))),
4890                (
4891                    Nibbles::from_nibbles([0x5, 0x0]),
4892                    SparseNode::new_leaf(Nibbles::from_nibbles([0x1, 0x2])),
4893                ),
4894                (
4895                    Nibbles::from_nibbles([0x5, 0x1]),
4896                    SparseNode::new_leaf(Nibbles::from_nibbles([0x3, 0x4])),
4897                ),
4898            ]
4899            .into_iter(),
4900        );
4901
4902        let provider = MockTrieNodeProvider::new();
4903
4904        // Remove the leaf with a full path of 0x5012
4905        let leaf_full_path = Nibbles::from_nibbles([0x5, 0x0, 0x1, 0x2]);
4906        trie.remove_leaf(&leaf_full_path, provider).unwrap();
4907
4908        let upper_subtrie = &trie.upper_subtrie;
4909
4910        // Check that both lower subtries were removed. 0x50 should have been removed because
4911        // removing its leaf made it empty. 0x51 should have been removed after its own leaf was
4912        // collapsed into the upper trie, leaving it also empty.
4913        assert_matches!(trie.lower_subtries[0x50].as_revealed_ref(), None);
4914        assert_matches!(trie.lower_subtries[0x51].as_revealed_ref(), None);
4915
4916        // Check that the other leaf's value was moved to the upper trie
4917        let other_leaf_full_value = Nibbles::from_nibbles([0x5, 0x1, 0x3, 0x4]);
4918        assert_matches!(upper_subtrie.inner.values.get(&other_leaf_full_value), Some(_));
4919
4920        // Check that the extension node collapsed into a leaf node
4921        assert_matches!(
4922            upper_subtrie.nodes.get(&Nibbles::default()),
4923            Some(SparseNode::Leaf{ key, ..})
4924            if key == &Nibbles::from_nibbles([0x5, 0x1, 0x3, 0x4])
4925        );
4926
4927        // Check that intermediate nodes were removed
4928        assert_matches!(upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x5])), None);
4929    }
4930
4931    #[test]
4932    fn test_remove_leaf_branch_on_branch() {
4933        //
4934        // 0x:      Branch (Mask = 0101)
4935        // 0x0:     ├── 0 -> Leaf (Key = 12)
4936        // 0x2:     └── 2 -> Branch (Mask = 0011)
4937        // 0x20:        ├── 0 -> Leaf (Key = 34)
4938        // 0x21:        └── 1 -> Leaf (Key = 56)
4939        //
4940        // After removing 0x2034, the inner branch becomes a leaf
4941        //
4942        let mut trie = new_test_trie(
4943            [
4944                (Nibbles::default(), SparseNode::new_branch(TrieMask::new(0b0101))),
4945                (
4946                    Nibbles::from_nibbles([0x0]),
4947                    SparseNode::new_leaf(Nibbles::from_nibbles([0x1, 0x2])),
4948                ),
4949                (Nibbles::from_nibbles([0x2]), SparseNode::new_branch(TrieMask::new(0b0011))),
4950                (
4951                    Nibbles::from_nibbles([0x2, 0x0]),
4952                    SparseNode::new_leaf(Nibbles::from_nibbles([0x3, 0x4])),
4953                ),
4954                (
4955                    Nibbles::from_nibbles([0x2, 0x1]),
4956                    SparseNode::new_leaf(Nibbles::from_nibbles([0x5, 0x6])),
4957                ),
4958            ]
4959            .into_iter(),
4960        );
4961
4962        let provider = MockTrieNodeProvider::new();
4963
4964        // Remove the leaf with a full path of 0x2034
4965        let leaf_full_path = Nibbles::from_nibbles([0x2, 0x0, 0x3, 0x4]);
4966        trie.remove_leaf(&leaf_full_path, provider).unwrap();
4967
4968        let upper_subtrie = &trie.upper_subtrie;
4969
4970        // Check that both lower subtries were removed. 0x20 should have been removed because
4971        // removing its leaf made it empty. 0x21 should have been removed after its own leaf was
4972        // collapsed into the upper trie, leaving it also empty.
4973        assert_matches!(trie.lower_subtries[0x20].as_revealed_ref(), None);
4974        assert_matches!(trie.lower_subtries[0x21].as_revealed_ref(), None);
4975
4976        // Check that the other leaf's value was moved to the upper trie
4977        let other_leaf_full_value = Nibbles::from_nibbles([0x2, 0x1, 0x5, 0x6]);
4978        assert_matches!(upper_subtrie.inner.values.get(&other_leaf_full_value), Some(_));
4979
4980        // Check that the root branch still exists unchanged
4981        assert_matches!(
4982            upper_subtrie.nodes.get(&Nibbles::default()),
4983            Some(SparseNode::Branch{ state_mask, .. })
4984            if *state_mask == 0b0101.into()
4985        );
4986
4987        // Check that the inner branch became an extension
4988        assert_matches!(
4989            upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x2])),
4990            Some(SparseNode::Leaf{ key, ..})
4991            if key == &Nibbles::from_nibbles([0x1, 0x5, 0x6])
4992        );
4993    }
4994
4995    #[test]
4996    fn test_remove_leaf_lower_subtrie_root_path_update() {
4997        //
4998        // 0x:        Extension (Key = 123, root of lower subtrie)
4999        // 0x123:     └── Branch (Mask = 0011000)
5000        // 0x1233:        ├── 3 -> Leaf (Key = [])
5001        // 0x1234:        └── 4 -> Extension (Key = 5)
5002        // 0x12345:           └── Branch (Mask = 0011)
5003        // 0x123450:              ├── 0 -> Leaf (Key = [])
5004        // 0x123451:              └── 1 -> Leaf (Key = [])
5005        //
5006        // After removing leaf at 0x1233, the branch at 0x123 becomes an extension to 0x12345, which
5007        // then gets merged with the root extension at 0x. The lower subtrie's `path` field should
5008        // be updated from 0x123 to 0x12345.
5009        //
5010        let mut trie = new_test_trie(
5011            [
5012                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x1, 0x2, 0x3]))),
5013                (
5014                    Nibbles::from_nibbles([0x1, 0x2, 0x3]),
5015                    SparseNode::new_branch(TrieMask::new(0b0011000)),
5016                ),
5017                (
5018                    Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x3]),
5019                    SparseNode::new_leaf(Nibbles::default()),
5020                ),
5021                (
5022                    Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]),
5023                    SparseNode::new_ext(Nibbles::from_nibbles([0x5])),
5024                ),
5025                (
5026                    Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5]),
5027                    SparseNode::new_branch(TrieMask::new(0b0011)),
5028                ),
5029                (
5030                    Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5, 0x0]),
5031                    SparseNode::new_leaf(Nibbles::default()),
5032                ),
5033                (
5034                    Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5, 0x1]),
5035                    SparseNode::new_leaf(Nibbles::default()),
5036                ),
5037            ]
5038            .into_iter(),
5039        );
5040
5041        let provider = MockTrieNodeProvider::new();
5042
5043        // Verify initial state - the lower subtrie's path should be 0x123
5044        let lower_subtrie_root_path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
5045        assert_matches!(
5046            trie.lower_subtrie_for_path_mut(&lower_subtrie_root_path),
5047            Some(subtrie)
5048            if subtrie.path == lower_subtrie_root_path
5049        );
5050
5051        // Remove the leaf at 0x1233
5052        let leaf_full_path = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x3]);
5053        trie.remove_leaf(&leaf_full_path, provider).unwrap();
5054
5055        // After removal:
5056        // 1. The branch at 0x123 should become an extension to 0x12345
5057        // 2. That extension should merge with the root extension at 0x
5058        // 3. The lower subtrie's path should be updated to 0x12345
5059        let lower_subtrie = trie.lower_subtries[0x12].as_revealed_ref().unwrap();
5060        assert_eq!(lower_subtrie.path, Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5]));
5061
5062        // Verify the root extension now points all the way to 0x12345
5063        assert_matches!(
5064            trie.upper_subtrie.nodes.get(&Nibbles::default()),
5065            Some(SparseNode::Extension { key, .. })
5066            if key == &Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5])
5067        );
5068
5069        // Verify the branch at 0x12345 hasn't been modified
5070        assert_matches!(
5071            lower_subtrie.nodes.get(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5])),
5072            Some(SparseNode::Branch { state_mask, .. })
5073            if state_mask == &TrieMask::new(0b0011)
5074        );
5075    }
5076
5077    #[test]
5078    fn test_remove_leaf_remaining_child_needs_reveal() {
5079        //
5080        // 0x:      Branch (Mask = 0011)
5081        // 0x0:     ├── 0 -> Leaf (Key = 12)
5082        // 0x1:     └── 1 -> Hash (blinded leaf)
5083        //
5084        // After removing 0x012, the hash node needs to be revealed to collapse the branch
5085        //
5086        let mut trie = new_test_trie(
5087            [
5088                (Nibbles::default(), SparseNode::new_branch(TrieMask::new(0b0011))),
5089                (
5090                    Nibbles::from_nibbles([0x0]),
5091                    SparseNode::new_leaf(Nibbles::from_nibbles([0x1, 0x2])),
5092                ),
5093                (Nibbles::from_nibbles([0x1]), SparseNode::Hash(B256::repeat_byte(0xab))),
5094            ]
5095            .into_iter(),
5096        );
5097
5098        // Create a mock provider that will reveal the blinded leaf
5099        let mut provider = MockTrieNodeProvider::new();
5100        let revealed_leaf = create_leaf_node([0x3, 0x4], 42);
5101        let mut encoded = Vec::new();
5102        revealed_leaf.encode(&mut encoded);
5103        provider.add_revealed_node(
5104            Nibbles::from_nibbles([0x1]),
5105            RevealedNode { node: encoded.into(), tree_mask: None, hash_mask: None },
5106        );
5107
5108        // Remove the leaf with a full path of 0x012
5109        let leaf_full_path = Nibbles::from_nibbles([0x0, 0x1, 0x2]);
5110        trie.remove_leaf(&leaf_full_path, provider).unwrap();
5111
5112        let upper_subtrie = &trie.upper_subtrie;
5113
5114        // Check that the leaf value was removed
5115        assert_matches!(upper_subtrie.inner.values.get(&leaf_full_path), None);
5116
5117        // Check that the branch node collapsed into a leaf node with the revealed child's key
5118        assert_matches!(
5119            upper_subtrie.nodes.get(&Nibbles::default()),
5120            Some(SparseNode::Leaf{ key, ..})
5121            if key == &Nibbles::from_nibbles([0x1, 0x3, 0x4])
5122        );
5123
5124        // Check that the remaining child node was removed (since it was merged)
5125        assert_matches!(upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x1])), None);
5126    }
5127
5128    #[test]
5129    fn test_remove_leaf_root() {
5130        //
5131        // 0x:      Leaf (Key = 123)
5132        //
5133        // After removing 0x123, the trie becomes empty
5134        //
5135        let mut trie = new_test_trie(core::iter::once((
5136            Nibbles::default(),
5137            SparseNode::new_leaf(Nibbles::from_nibbles([0x1, 0x2, 0x3])),
5138        )));
5139
5140        let provider = MockTrieNodeProvider::new();
5141
5142        // Remove the leaf with a full key of 0x123
5143        let leaf_full_path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
5144        trie.remove_leaf(&leaf_full_path, provider).unwrap();
5145
5146        let upper_subtrie = &trie.upper_subtrie;
5147
5148        // Check that the leaf value was removed
5149        assert_matches!(upper_subtrie.inner.values.get(&leaf_full_path), None);
5150
5151        // Check that the root node was changed to Empty
5152        assert_matches!(upper_subtrie.nodes.get(&Nibbles::default()), Some(SparseNode::Empty));
5153    }
5154
5155    #[test]
5156    fn test_remove_leaf_unsets_hash_along_path() {
5157        //
5158        // Creates a trie structure:
5159        // 0x:      Branch (with hash set)
5160        // 0x0:     ├── Extension (with hash set)
5161        // 0x01:    │   └── Branch (with hash set)
5162        // 0x012:   │       ├── Leaf (Key = 34, with hash set)
5163        // 0x013:   │       ├── Leaf (Key = 56, with hash set)
5164        // 0x014:   │       └── Leaf (Key = 78, with hash set)
5165        // 0x1:     └── Leaf (Key = 78, with hash set)
5166        //
5167        // When removing leaf at 0x01234, all nodes along the path (root branch,
5168        // extension at 0x0, branch at 0x01) should have their hash field unset
5169        //
5170
5171        let mut trie = new_test_trie(
5172            [
5173                (
5174                    Nibbles::default(),
5175                    SparseNode::Branch {
5176                        state_mask: TrieMask::new(0b0011),
5177                        hash: Some(B256::repeat_byte(0x10)),
5178                        store_in_db_trie: None,
5179                    },
5180                ),
5181                (
5182                    Nibbles::from_nibbles([0x0]),
5183                    SparseNode::Extension {
5184                        key: Nibbles::from_nibbles([0x1]),
5185                        hash: Some(B256::repeat_byte(0x20)),
5186                        store_in_db_trie: None,
5187                    },
5188                ),
5189                (
5190                    Nibbles::from_nibbles([0x0, 0x1]),
5191                    SparseNode::Branch {
5192                        state_mask: TrieMask::new(0b11100),
5193                        hash: Some(B256::repeat_byte(0x30)),
5194                        store_in_db_trie: None,
5195                    },
5196                ),
5197                (
5198                    Nibbles::from_nibbles([0x0, 0x1, 0x2]),
5199                    SparseNode::Leaf {
5200                        key: Nibbles::from_nibbles([0x3, 0x4]),
5201                        hash: Some(B256::repeat_byte(0x40)),
5202                    },
5203                ),
5204                (
5205                    Nibbles::from_nibbles([0x0, 0x1, 0x3]),
5206                    SparseNode::Leaf {
5207                        key: Nibbles::from_nibbles([0x5, 0x6]),
5208                        hash: Some(B256::repeat_byte(0x50)),
5209                    },
5210                ),
5211                (
5212                    Nibbles::from_nibbles([0x0, 0x1, 0x4]),
5213                    SparseNode::Leaf {
5214                        key: Nibbles::from_nibbles([0x6, 0x7]),
5215                        hash: Some(B256::repeat_byte(0x60)),
5216                    },
5217                ),
5218                (
5219                    Nibbles::from_nibbles([0x1]),
5220                    SparseNode::Leaf {
5221                        key: Nibbles::from_nibbles([0x7, 0x8]),
5222                        hash: Some(B256::repeat_byte(0x70)),
5223                    },
5224                ),
5225            ]
5226            .into_iter(),
5227        );
5228
5229        let provider = MockTrieNodeProvider::new();
5230
5231        // Remove a leaf which does not exist; this should have no effect.
5232        trie.remove_leaf(&Nibbles::from_nibbles([0x0, 0x1, 0x2, 0x3, 0x4, 0xF]), &provider)
5233            .unwrap();
5234        for (path, node) in trie.all_nodes() {
5235            assert!(node.hash().is_some(), "path {path:?} should still have a hash");
5236        }
5237
5238        // Remove the leaf at path 0x01234
5239        let leaf_full_path = Nibbles::from_nibbles([0x0, 0x1, 0x2, 0x3, 0x4]);
5240        trie.remove_leaf(&leaf_full_path, &provider).unwrap();
5241
5242        let upper_subtrie = &trie.upper_subtrie;
5243        let lower_subtrie_10 = trie.lower_subtries[0x01].as_revealed_ref().unwrap();
5244
5245        // Verify that hash fields are unset for all nodes along the path to the removed leaf
5246        assert_matches!(
5247            upper_subtrie.nodes.get(&Nibbles::default()),
5248            Some(SparseNode::Branch { hash: None, .. })
5249        );
5250        assert_matches!(
5251            upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x0])),
5252            Some(SparseNode::Extension { hash: None, .. })
5253        );
5254        assert_matches!(
5255            lower_subtrie_10.nodes.get(&Nibbles::from_nibbles([0x0, 0x1])),
5256            Some(SparseNode::Branch { hash: None, .. })
5257        );
5258
5259        // Verify that nodes not on the path still have their hashes
5260        assert_matches!(
5261            upper_subtrie.nodes.get(&Nibbles::from_nibbles([0x1])),
5262            Some(SparseNode::Leaf { hash: Some(_), .. })
5263        );
5264        assert_matches!(
5265            lower_subtrie_10.nodes.get(&Nibbles::from_nibbles([0x0, 0x1, 0x3])),
5266            Some(SparseNode::Leaf { hash: Some(_), .. })
5267        );
5268        assert_matches!(
5269            lower_subtrie_10.nodes.get(&Nibbles::from_nibbles([0x0, 0x1, 0x4])),
5270            Some(SparseNode::Leaf { hash: Some(_), .. })
5271        );
5272    }
5273
5274    #[test]
5275    fn test_remove_leaf_remaining_extension_node_child_is_revealed() {
5276        let branch_path = Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7]);
5277        let removed_branch_path = Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7, 0x2]);
5278
5279        // Convert the logs into reveal_nodes call on a fresh ParallelSparseTrie
5280        let mut nodes = vec![
5281            // Branch at 0x4f8807
5282            ProofTrieNode {
5283                path: branch_path,
5284                node: {
5285                    TrieNode::Branch(BranchNode::new(
5286                        vec![
5287                            RlpNode::word_rlp(&B256::from(hex!(
5288                                "dede882d52f0e0eddfb5b89293a10c87468b4a73acd0d4ae550054a92353f6d5"
5289                            ))),
5290                            RlpNode::word_rlp(&B256::from(hex!(
5291                                "8746f18e465e2eed16117306b6f2eef30bc9d2978aee4a7838255e39c41a3222"
5292                            ))),
5293                            RlpNode::word_rlp(&B256::from(hex!(
5294                                "35a4ea861548af5f0262a9b6d619b4fc88fce6531cbd004eab1530a73f34bbb1"
5295                            ))),
5296                            RlpNode::word_rlp(&B256::from(hex!(
5297                                "47d5c2bf9eea5c1ee027e4740c2b86159074a27d52fd2f6a8a8c86c77e48006f"
5298                            ))),
5299                            RlpNode::word_rlp(&B256::from(hex!(
5300                                "eb76a359b216e1d86b1f2803692a9fe8c3d3f97a9fe6a82b396e30344febc0c1"
5301                            ))),
5302                            RlpNode::word_rlp(&B256::from(hex!(
5303                                "437656f2697f167b23e33cb94acc8550128cfd647fc1579d61e982cb7616b8bc"
5304                            ))),
5305                            RlpNode::word_rlp(&B256::from(hex!(
5306                                "45a1ac2faf15ea8a4da6f921475974e0379f39c3d08166242255a567fa88ce6c"
5307                            ))),
5308                            RlpNode::word_rlp(&B256::from(hex!(
5309                                "7dbb299d714d3dfa593f53bc1b8c66d5c401c30a0b5587b01254a56330361395"
5310                            ))),
5311                            RlpNode::word_rlp(&B256::from(hex!(
5312                                "ae407eb14a74ed951c9949c1867fb9ee9ba5d5b7e03769eaf3f29c687d080429"
5313                            ))),
5314                            RlpNode::word_rlp(&B256::from(hex!(
5315                                "768d0fe1003f0e85d3bc76e4a1fa0827f63b10ca9bca52d56c2b1cceb8eb8b08"
5316                            ))),
5317                            RlpNode::word_rlp(&B256::from(hex!(
5318                                "e5127935143493d5094f4da6e4f7f5a0f62d524fbb61e7bb9fb63d8a166db0f3"
5319                            ))),
5320                            RlpNode::word_rlp(&B256::from(hex!(
5321                                "7f3698297308664fbc1b9e2c41d097fbd57d8f364c394f6ad7c71b10291fbf42"
5322                            ))),
5323                            RlpNode::word_rlp(&B256::from(hex!(
5324                                "4a2bc7e19cec63cb5ef5754add0208959b50bcc79f13a22a370f77b277dbe6db"
5325                            ))),
5326                            RlpNode::word_rlp(&B256::from(hex!(
5327                                "40764b8c48de59258e62a3371909a107e76e1b5e847cfa94dbc857e9fd205103"
5328                            ))),
5329                            RlpNode::word_rlp(&B256::from(hex!(
5330                                "2985dca29a7616920d95c43ab62eb013a40e6a0c88c284471e4c3bd22f3b9b25"
5331                            ))),
5332                            RlpNode::word_rlp(&B256::from(hex!(
5333                                "1b6511f7a385e79477239f7dd4a49f52082ecac05aa5bd0de18b1d55fe69d10c"
5334                            ))),
5335                        ],
5336                        TrieMask::new(0b1111111111111111),
5337                    ))
5338                },
5339                masks: Some(BranchNodeMasks {
5340                    hash_mask: TrieMask::new(0b1111111111111111),
5341                    tree_mask: TrieMask::new(0b0011110100100101),
5342                }),
5343            },
5344            // Branch at 0x4f88072
5345            ProofTrieNode {
5346                path: removed_branch_path,
5347                node: {
5348                    let stack = vec![
5349                        RlpNode::word_rlp(&B256::from(hex!(
5350                            "15fd4993a41feff1af3b629b32572ab05acddd97c681d82ec2eb89c8a8e3ab9e"
5351                        ))),
5352                        RlpNode::word_rlp(&B256::from(hex!(
5353                            "a272b0b94ced4e6ec7adb41719850cf4a167ad8711d0dda6a810d129258a0d94"
5354                        ))),
5355                    ];
5356                    let branch_node = BranchNode::new(stack, TrieMask::new(0b0001000000000100));
5357                    TrieNode::Branch(branch_node)
5358                },
5359                masks: Some(BranchNodeMasks {
5360                    hash_mask: TrieMask::new(0b0000000000000000),
5361                    tree_mask: TrieMask::new(0b0000000000000100),
5362                }),
5363            },
5364            // Extension at 0x4f880722
5365            ProofTrieNode {
5366                path: Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7, 0x2, 0x2]),
5367                node: {
5368                    let extension_node = ExtensionNode::new(
5369                        Nibbles::from_nibbles([0x6]),
5370                        RlpNode::word_rlp(&B256::from(hex!(
5371                            "56fab2b106a97eae9c7197f86d03bca292da6e0ac725b783082f7d950cc4e0fc"
5372                        ))),
5373                    );
5374                    TrieNode::Extension(extension_node)
5375                },
5376                masks: None,
5377            },
5378            // Leaf at 0x4f88072c
5379            ProofTrieNode {
5380                path: Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7, 0x2, 0xc]),
5381                node: {
5382                    let leaf_node = LeafNode::new(
5383                        Nibbles::from_nibbles([
5384                            0x0, 0x7, 0x7, 0xf, 0x8, 0x6, 0x6, 0x1, 0x3, 0x0, 0x8, 0x8, 0xd, 0xf,
5385                            0xc, 0xa, 0xe, 0x6, 0x4, 0x8, 0xa, 0xb, 0xe, 0x8, 0x3, 0x1, 0xf, 0xa,
5386                            0xd, 0xc, 0xa, 0x5, 0x5, 0xa, 0xd, 0x4, 0x3, 0xa, 0xb, 0x1, 0x6, 0x5,
5387                            0xd, 0x1, 0x6, 0x8, 0x0, 0xd, 0xd, 0x5, 0x6, 0x7, 0xb, 0x5, 0xd, 0x6,
5388                        ]),
5389                        hex::decode("8468d3971d").unwrap(),
5390                    );
5391                    TrieNode::Leaf(leaf_node)
5392                },
5393                masks: None,
5394            },
5395        ];
5396
5397        // Create a fresh ParallelSparseTrie
5398        let mut trie = ParallelSparseTrie::from_root(
5399            TrieNode::Extension(ExtensionNode::new(
5400                Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7]),
5401                RlpNode::word_rlp(&B256::from(hex!(
5402                    "56fab2b106a97eae9c7197f86d03bca292da6e0ac725b783082f7d950cc4e0fc"
5403                ))),
5404            )),
5405            None,
5406            true,
5407        )
5408        .unwrap();
5409
5410        // Call reveal_nodes
5411        trie.reveal_nodes(&mut nodes).unwrap();
5412
5413        // Remove the leaf at "0x4f88072c077f86613088dfcae648abe831fadca55ad43ab165d1680dd567b5d6"
5414        let leaf_key = Nibbles::from_nibbles([
5415            0x4, 0xf, 0x8, 0x8, 0x0, 0x7, 0x2, 0xc, 0x0, 0x7, 0x7, 0xf, 0x8, 0x6, 0x6, 0x1, 0x3,
5416            0x0, 0x8, 0x8, 0xd, 0xf, 0xc, 0xa, 0xe, 0x6, 0x4, 0x8, 0xa, 0xb, 0xe, 0x8, 0x3, 0x1,
5417            0xf, 0xa, 0xd, 0xc, 0xa, 0x5, 0x5, 0xa, 0xd, 0x4, 0x3, 0xa, 0xb, 0x1, 0x6, 0x5, 0xd,
5418            0x1, 0x6, 0x8, 0x0, 0xd, 0xd, 0x5, 0x6, 0x7, 0xb, 0x5, 0xd, 0x6,
5419        ]);
5420
5421        let mut provider = MockTrieNodeProvider::new();
5422        let revealed_branch = create_branch_node_with_children(&[], []);
5423        let mut encoded = Vec::new();
5424        revealed_branch.encode(&mut encoded);
5425        provider.add_revealed_node(
5426            Nibbles::from_nibbles([0x4, 0xf, 0x8, 0x8, 0x0, 0x7, 0x2, 0x2, 0x6]),
5427            RevealedNode {
5428                node: encoded.into(),
5429                tree_mask: None,
5430                // Give it a fake hashmask so that it appears like it will be stored in the db
5431                hash_mask: Some(TrieMask::new(0b1111)),
5432            },
5433        );
5434
5435        trie.remove_leaf(&leaf_key, provider).unwrap();
5436
5437        // Calculate root so that updates are calculated.
5438        trie.root();
5439
5440        // Take updates and assert they are correct
5441        let updates = trie.take_updates();
5442        assert_eq!(
5443            updates.removed_nodes.into_iter().collect::<Vec<_>>(),
5444            vec![removed_branch_path]
5445        );
5446        assert_eq!(updates.updated_nodes.len(), 1);
5447        let updated_node = updates.updated_nodes.get(&branch_path).unwrap();
5448
5449        // Second bit must be set, indicating that the extension's child is in the db
5450        assert_eq!(updated_node.tree_mask, TrieMask::new(0b011110100100101),)
5451    }
5452
5453    #[test]
5454    fn test_parallel_sparse_trie_root() {
5455        // Step 1: Create the trie structure
5456        // Extension node at 0x with key 0x2 (goes to upper subtrie)
5457        let extension_path = Nibbles::new();
5458        let extension_key = Nibbles::from_nibbles([0x2]);
5459
5460        // Branch node at 0x2 with children 0 and 1 (goes to upper subtrie)
5461        let branch_path = Nibbles::from_nibbles([0x2]);
5462
5463        // Leaf nodes at 0x20 and 0x21 (go to lower subtries)
5464        let leaf_1_path = Nibbles::from_nibbles([0x2, 0x0]);
5465        let leaf_1_key = Nibbles::from_nibbles(vec![0; 62]); // Remaining key
5466        let leaf_1_full_path = Nibbles::from_nibbles([vec![0x2, 0x0], vec![0; 62]].concat());
5467
5468        let leaf_2_path = Nibbles::from_nibbles([0x2, 0x1]);
5469        let leaf_2_key = Nibbles::from_nibbles(vec![0; 62]); // Remaining key
5470        let leaf_2_full_path = Nibbles::from_nibbles([vec![0x2, 0x1], vec![0; 62]].concat());
5471
5472        // Create accounts
5473        let account_1 = create_account(1);
5474        let account_2 = create_account(2);
5475
5476        // Create leaf nodes
5477        let leaf_1 = create_leaf_node(leaf_1_key.to_vec(), account_1.nonce);
5478        let leaf_2 = create_leaf_node(leaf_2_key.to_vec(), account_2.nonce);
5479
5480        // Create branch node with children at indices 0 and 1
5481        let branch = create_branch_node_with_children(
5482            &[0, 1],
5483            vec![
5484                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1)),
5485                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2)),
5486            ],
5487        );
5488
5489        // Create extension node pointing to branch
5490        let extension = create_extension_node(
5491            extension_key.to_vec(),
5492            RlpNode::from_rlp(&alloy_rlp::encode(&branch)).as_hash().unwrap(),
5493        );
5494
5495        // Step 2: Reveal nodes in the trie
5496        let mut trie = ParallelSparseTrie::from_root(extension, None, true).unwrap();
5497        trie.reveal_nodes(&mut [
5498            ProofTrieNode { path: branch_path, node: branch, masks: None },
5499            ProofTrieNode { path: leaf_1_path, node: leaf_1, masks: None },
5500            ProofTrieNode { path: leaf_2_path, node: leaf_2, masks: None },
5501        ])
5502        .unwrap();
5503
5504        // Step 3: Reset hashes for all revealed nodes to test actual hash calculation
5505        // Reset upper subtrie node hashes
5506        trie.upper_subtrie.nodes.get_mut(&extension_path).unwrap().set_hash(None);
5507        trie.upper_subtrie.nodes.get_mut(&branch_path).unwrap().set_hash(None);
5508
5509        // Reset lower subtrie node hashes
5510        let leaf_1_subtrie_idx = path_subtrie_index_unchecked(&leaf_1_path);
5511        let leaf_2_subtrie_idx = path_subtrie_index_unchecked(&leaf_2_path);
5512
5513        trie.lower_subtries[leaf_1_subtrie_idx]
5514            .as_revealed_mut()
5515            .unwrap()
5516            .nodes
5517            .get_mut(&leaf_1_path)
5518            .unwrap()
5519            .set_hash(None);
5520        trie.lower_subtries[leaf_2_subtrie_idx]
5521            .as_revealed_mut()
5522            .unwrap()
5523            .nodes
5524            .get_mut(&leaf_2_path)
5525            .unwrap()
5526            .set_hash(None);
5527
5528        // Step 4: Add changed leaf node paths to prefix set
5529        trie.prefix_set.insert(leaf_1_full_path);
5530        trie.prefix_set.insert(leaf_2_full_path);
5531
5532        // Step 5: Calculate root using our implementation
5533        let root = trie.root();
5534
5535        // Step 6: Calculate root using HashBuilder for comparison
5536        let (hash_builder_root, _, _proof_nodes, _, _) = run_hash_builder(
5537            [(leaf_1_full_path, account_1), (leaf_2_full_path, account_2)],
5538            NoopAccountTrieCursor::default(),
5539            Default::default(),
5540            [extension_path, branch_path, leaf_1_full_path, leaf_2_full_path],
5541        );
5542
5543        // Step 7: Verify the roots match
5544        assert_eq!(root, hash_builder_root);
5545
5546        // Verify hashes were computed
5547        let leaf_1_subtrie = trie.lower_subtries[leaf_1_subtrie_idx].as_revealed_ref().unwrap();
5548        let leaf_2_subtrie = trie.lower_subtries[leaf_2_subtrie_idx].as_revealed_ref().unwrap();
5549        assert!(trie.upper_subtrie.nodes.get(&extension_path).unwrap().hash().is_some());
5550        assert!(trie.upper_subtrie.nodes.get(&branch_path).unwrap().hash().is_some());
5551        assert!(leaf_1_subtrie.nodes.get(&leaf_1_path).unwrap().hash().is_some());
5552        assert!(leaf_2_subtrie.nodes.get(&leaf_2_path).unwrap().hash().is_some());
5553    }
5554
5555    #[test]
5556    fn sparse_trie_empty_update_one() {
5557        let ctx = ParallelSparseTrieTestContext;
5558
5559        let key = Nibbles::unpack(B256::with_last_byte(42));
5560        let value = || Account::default();
5561        let value_encoded = || {
5562            let mut account_rlp = Vec::new();
5563            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5564            account_rlp
5565        };
5566
5567        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5568            run_hash_builder(
5569                [(key, value())],
5570                NoopAccountTrieCursor::default(),
5571                Default::default(),
5572                [key],
5573            );
5574
5575        let mut sparse = ParallelSparseTrie::default().with_updates(true);
5576        ctx.update_leaves(&mut sparse, [(key, value_encoded())]);
5577        ctx.assert_with_hash_builder(
5578            &mut sparse,
5579            hash_builder_root,
5580            hash_builder_updates,
5581            hash_builder_proof_nodes,
5582        );
5583    }
5584
5585    #[test]
5586    fn sparse_trie_empty_update_multiple_lower_nibbles() {
5587        let ctx = ParallelSparseTrieTestContext;
5588
5589        let paths = (0..=16).map(|b| Nibbles::unpack(B256::with_last_byte(b))).collect::<Vec<_>>();
5590        let value = || Account::default();
5591        let value_encoded = || {
5592            let mut account_rlp = Vec::new();
5593            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5594            account_rlp
5595        };
5596
5597        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5598            run_hash_builder(
5599                paths.iter().copied().zip(core::iter::repeat_with(value)),
5600                NoopAccountTrieCursor::default(),
5601                Default::default(),
5602                paths.clone(),
5603            );
5604
5605        let mut sparse = ParallelSparseTrie::default().with_updates(true);
5606        ctx.update_leaves(
5607            &mut sparse,
5608            paths.into_iter().zip(core::iter::repeat_with(value_encoded)),
5609        );
5610
5611        ctx.assert_with_hash_builder(
5612            &mut sparse,
5613            hash_builder_root,
5614            hash_builder_updates,
5615            hash_builder_proof_nodes,
5616        );
5617    }
5618
5619    #[test]
5620    fn sparse_trie_empty_update_multiple_upper_nibbles() {
5621        let paths = (239..=255).map(|b| Nibbles::unpack(B256::repeat_byte(b))).collect::<Vec<_>>();
5622        let value = || Account::default();
5623        let value_encoded = || {
5624            let mut account_rlp = Vec::new();
5625            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5626            account_rlp
5627        };
5628
5629        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5630            run_hash_builder(
5631                paths.iter().copied().zip(core::iter::repeat_with(value)),
5632                NoopAccountTrieCursor::default(),
5633                Default::default(),
5634                paths.clone(),
5635            );
5636
5637        let provider = DefaultTrieNodeProvider;
5638        let mut sparse = ParallelSparseTrie::default().with_updates(true);
5639        for path in &paths {
5640            sparse.update_leaf(*path, value_encoded(), &provider).unwrap();
5641        }
5642        let sparse_root = sparse.root();
5643        let sparse_updates = sparse.take_updates();
5644
5645        assert_eq!(sparse_root, hash_builder_root);
5646        assert_eq!(sparse_updates.updated_nodes, hash_builder_updates.account_nodes);
5647        assert_eq_parallel_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes);
5648    }
5649
5650    #[test]
5651    fn sparse_trie_empty_update_multiple() {
5652        let ctx = ParallelSparseTrieTestContext;
5653
5654        let paths = (0..=255)
5655            .map(|b| {
5656                Nibbles::unpack(if b % 2 == 0 {
5657                    B256::repeat_byte(b)
5658                } else {
5659                    B256::with_last_byte(b)
5660                })
5661            })
5662            .collect::<Vec<_>>();
5663        let value = || Account::default();
5664        let value_encoded = || {
5665            let mut account_rlp = Vec::new();
5666            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5667            account_rlp
5668        };
5669
5670        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5671            run_hash_builder(
5672                paths.iter().sorted_unstable().copied().zip(core::iter::repeat_with(value)),
5673                NoopAccountTrieCursor::default(),
5674                Default::default(),
5675                paths.clone(),
5676            );
5677
5678        let mut sparse = ParallelSparseTrie::default().with_updates(true);
5679        ctx.update_leaves(
5680            &mut sparse,
5681            paths.iter().copied().zip(core::iter::repeat_with(value_encoded)),
5682        );
5683        ctx.assert_with_hash_builder(
5684            &mut sparse,
5685            hash_builder_root,
5686            hash_builder_updates,
5687            hash_builder_proof_nodes,
5688        );
5689    }
5690
5691    #[test]
5692    fn sparse_trie_empty_update_repeated() {
5693        let ctx = ParallelSparseTrieTestContext;
5694
5695        let paths = (0..=255).map(|b| Nibbles::unpack(B256::repeat_byte(b))).collect::<Vec<_>>();
5696        let old_value = Account { nonce: 1, ..Default::default() };
5697        let old_value_encoded = {
5698            let mut account_rlp = Vec::new();
5699            old_value.into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5700            account_rlp
5701        };
5702        let new_value = Account { nonce: 2, ..Default::default() };
5703        let new_value_encoded = {
5704            let mut account_rlp = Vec::new();
5705            new_value.into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
5706            account_rlp
5707        };
5708
5709        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5710            run_hash_builder(
5711                paths.iter().copied().zip(core::iter::repeat_with(|| old_value)),
5712                NoopAccountTrieCursor::default(),
5713                Default::default(),
5714                paths.clone(),
5715            );
5716
5717        let mut sparse = ParallelSparseTrie::default().with_updates(true);
5718        ctx.update_leaves(
5719            &mut sparse,
5720            paths.iter().copied().zip(core::iter::repeat(old_value_encoded)),
5721        );
5722        ctx.assert_with_hash_builder(
5723            &mut sparse,
5724            hash_builder_root,
5725            hash_builder_updates,
5726            hash_builder_proof_nodes,
5727        );
5728
5729        let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
5730            run_hash_builder(
5731                paths.iter().copied().zip(core::iter::repeat(new_value)),
5732                NoopAccountTrieCursor::default(),
5733                Default::default(),
5734                paths.clone(),
5735            );
5736
5737        ctx.update_leaves(
5738            &mut sparse,
5739            paths.iter().copied().zip(core::iter::repeat(new_value_encoded)),
5740        );
5741        ctx.assert_with_hash_builder(
5742            &mut sparse,
5743            hash_builder_root,
5744            hash_builder_updates,
5745            hash_builder_proof_nodes,
5746        );
5747    }
5748
5749    #[test]
5750    fn sparse_trie_remove_leaf() {
5751        let ctx = ParallelSparseTrieTestContext;
5752        let provider = DefaultTrieNodeProvider;
5753        let mut sparse = ParallelSparseTrie::default();
5754
5755        let value = alloy_rlp::encode_fixed_size(&U256::ZERO).to_vec();
5756
5757        ctx.update_leaves(
5758            &mut sparse,
5759            [
5760                (Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]), value.clone()),
5761                (Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]), value.clone()),
5762                (Nibbles::from_nibbles([0x5, 0x2, 0x0, 0x1, 0x3]), value.clone()),
5763                (Nibbles::from_nibbles([0x5, 0x3, 0x1, 0x0, 0x2]), value.clone()),
5764                (Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2]), value.clone()),
5765                (Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2, 0x0]), value),
5766            ],
5767        );
5768
5769        // Extension (Key = 5)
5770        // └── Branch (Mask = 1011)
5771        //     ├── 0 -> Extension (Key = 23)
5772        //     │        └── Branch (Mask = 0101)
5773        //     │              ├── 1 -> Leaf (Key = 1, Path = 50231)
5774        //     │              └── 3 -> Leaf (Key = 3, Path = 50233)
5775        //     ├── 2 -> Leaf (Key = 013, Path = 52013)
5776        //     └── 3 -> Branch (Mask = 0101)
5777        //                ├── 1 -> Leaf (Key = 3102, Path = 53102)
5778        //                └── 3 -> Branch (Mask = 1010)
5779        //                       ├── 0 -> Leaf (Key = 3302, Path = 53302)
5780        //                       └── 2 -> Leaf (Key = 3320, Path = 53320)
5781        pretty_assertions::assert_eq!(
5782            parallel_sparse_trie_nodes(&sparse)
5783                .into_iter()
5784                .map(|(k, v)| (*k, v.clone()))
5785                .collect::<BTreeMap<_, _>>(),
5786            BTreeMap::from_iter([
5787                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
5788                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1101.into())),
5789                (
5790                    Nibbles::from_nibbles([0x5, 0x0]),
5791                    SparseNode::new_ext(Nibbles::from_nibbles([0x2, 0x3]))
5792                ),
5793                (
5794                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3]),
5795                    SparseNode::new_branch(0b1010.into())
5796                ),
5797                (
5798                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]),
5799                    SparseNode::new_leaf(Nibbles::default())
5800                ),
5801                (
5802                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]),
5803                    SparseNode::new_leaf(Nibbles::default())
5804                ),
5805                (
5806                    Nibbles::from_nibbles([0x5, 0x2]),
5807                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x1, 0x3]))
5808                ),
5809                (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())),
5810                (
5811                    Nibbles::from_nibbles([0x5, 0x3, 0x1]),
5812                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2]))
5813                ),
5814                (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())),
5815                (
5816                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]),
5817                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2]))
5818                ),
5819                (
5820                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]),
5821                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0]))
5822                )
5823            ])
5824        );
5825
5826        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x2, 0x0, 0x1, 0x3]), &provider).unwrap();
5827
5828        // Extension (Key = 5)
5829        // └── Branch (Mask = 1001)
5830        //     ├── 0 -> Extension (Key = 23)
5831        //     │        └── Branch (Mask = 0101)
5832        //     │              ├── 1 -> Leaf (Key = 0231, Path = 50231)
5833        //     │              └── 3 -> Leaf (Key = 0233, Path = 50233)
5834        //     └── 3 -> Branch (Mask = 0101)
5835        //                ├── 1 -> Leaf (Key = 3102, Path = 53102)
5836        //                └── 3 -> Branch (Mask = 1010)
5837        //                       ├── 0 -> Leaf (Key = 3302, Path = 53302)
5838        //                       └── 2 -> Leaf (Key = 3320, Path = 53320)
5839        pretty_assertions::assert_eq!(
5840            parallel_sparse_trie_nodes(&sparse)
5841                .into_iter()
5842                .map(|(k, v)| (*k, v.clone()))
5843                .collect::<BTreeMap<_, _>>(),
5844            BTreeMap::from_iter([
5845                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
5846                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())),
5847                (
5848                    Nibbles::from_nibbles([0x5, 0x0]),
5849                    SparseNode::new_ext(Nibbles::from_nibbles([0x2, 0x3]))
5850                ),
5851                (
5852                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3]),
5853                    SparseNode::new_branch(0b1010.into())
5854                ),
5855                (
5856                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]),
5857                    SparseNode::new_leaf(Nibbles::default())
5858                ),
5859                (
5860                    Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]),
5861                    SparseNode::new_leaf(Nibbles::default())
5862                ),
5863                (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())),
5864                (
5865                    Nibbles::from_nibbles([0x5, 0x3, 0x1]),
5866                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2]))
5867                ),
5868                (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())),
5869                (
5870                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]),
5871                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2]))
5872                ),
5873                (
5874                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]),
5875                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0]))
5876                )
5877            ])
5878        );
5879
5880        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]), &provider).unwrap();
5881
5882        // Extension (Key = 5)
5883        // └── Branch (Mask = 1001)
5884        //     ├── 0 -> Leaf (Key = 0233, Path = 50233)
5885        //     └── 3 -> Branch (Mask = 0101)
5886        //                ├── 1 -> Leaf (Key = 3102, Path = 53102)
5887        //                └── 3 -> Branch (Mask = 1010)
5888        //                       ├── 0 -> Leaf (Key = 3302, Path = 53302)
5889        //                       └── 2 -> Leaf (Key = 3320, Path = 53320)
5890        pretty_assertions::assert_eq!(
5891            parallel_sparse_trie_nodes(&sparse)
5892                .into_iter()
5893                .map(|(k, v)| (*k, v.clone()))
5894                .collect::<BTreeMap<_, _>>(),
5895            BTreeMap::from_iter([
5896                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
5897                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())),
5898                (
5899                    Nibbles::from_nibbles([0x5, 0x0]),
5900                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3]))
5901                ),
5902                (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())),
5903                (
5904                    Nibbles::from_nibbles([0x5, 0x3, 0x1]),
5905                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2]))
5906                ),
5907                (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())),
5908                (
5909                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]),
5910                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2]))
5911                ),
5912                (
5913                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]),
5914                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0]))
5915                )
5916            ])
5917        );
5918
5919        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x3, 0x1, 0x0, 0x2]), &provider).unwrap();
5920
5921        // Extension (Key = 5)
5922        // └── Branch (Mask = 1001)
5923        //     ├── 0 -> Leaf (Key = 0233, Path = 50233)
5924        //     └── 3 -> Branch (Mask = 1010)
5925        //                ├── 0 -> Leaf (Key = 3302, Path = 53302)
5926        //                └── 2 -> Leaf (Key = 3320, Path = 53320)
5927        pretty_assertions::assert_eq!(
5928            parallel_sparse_trie_nodes(&sparse)
5929                .into_iter()
5930                .map(|(k, v)| (*k, v.clone()))
5931                .collect::<BTreeMap<_, _>>(),
5932            BTreeMap::from_iter([
5933                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
5934                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())),
5935                (
5936                    Nibbles::from_nibbles([0x5, 0x0]),
5937                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3]))
5938                ),
5939                (
5940                    Nibbles::from_nibbles([0x5, 0x3]),
5941                    SparseNode::new_ext(Nibbles::from_nibbles([0x3]))
5942                ),
5943                (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())),
5944                (
5945                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]),
5946                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2]))
5947                ),
5948                (
5949                    Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]),
5950                    SparseNode::new_leaf(Nibbles::from_nibbles([0x0]))
5951                )
5952            ])
5953        );
5954
5955        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2, 0x0]), &provider).unwrap();
5956
5957        // Extension (Key = 5)
5958        // └── Branch (Mask = 1001)
5959        //     ├── 0 -> Leaf (Key = 0233, Path = 50233)
5960        //     └── 3 -> Leaf (Key = 3302, Path = 53302)
5961        pretty_assertions::assert_eq!(
5962            parallel_sparse_trie_nodes(&sparse)
5963                .into_iter()
5964                .map(|(k, v)| (*k, v.clone()))
5965                .collect::<BTreeMap<_, _>>(),
5966            BTreeMap::from_iter([
5967                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))),
5968                (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())),
5969                (
5970                    Nibbles::from_nibbles([0x5, 0x0]),
5971                    SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3]))
5972                ),
5973                (
5974                    Nibbles::from_nibbles([0x5, 0x3]),
5975                    SparseNode::new_leaf(Nibbles::from_nibbles([0x3, 0x0, 0x2]))
5976                ),
5977            ])
5978        );
5979
5980        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]), &provider).unwrap();
5981
5982        // Leaf (Key = 53302)
5983        pretty_assertions::assert_eq!(
5984            parallel_sparse_trie_nodes(&sparse)
5985                .into_iter()
5986                .map(|(k, v)| (*k, v.clone()))
5987                .collect::<BTreeMap<_, _>>(),
5988            BTreeMap::from_iter([(
5989                Nibbles::default(),
5990                SparseNode::new_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2]))
5991            ),])
5992        );
5993
5994        sparse.remove_leaf(&Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2]), &provider).unwrap();
5995
5996        // Empty
5997        pretty_assertions::assert_eq!(
5998            parallel_sparse_trie_nodes(&sparse)
5999                .into_iter()
6000                .map(|(k, v)| (*k, v.clone()))
6001                .collect::<BTreeMap<_, _>>(),
6002            BTreeMap::from_iter([(Nibbles::default(), SparseNode::Empty)])
6003        );
6004    }
6005
6006    #[test]
6007    fn sparse_trie_remove_leaf_blinded() {
6008        let leaf = LeafNode::new(
6009            Nibbles::default(),
6010            alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec(),
6011        );
6012        let branch = TrieNode::Branch(BranchNode::new(
6013            vec![
6014                RlpNode::word_rlp(&B256::repeat_byte(1)),
6015                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(),
6016            ],
6017            TrieMask::new(0b11),
6018        ));
6019
6020        let provider = DefaultTrieNodeProvider;
6021        let mut sparse = ParallelSparseTrie::from_root(
6022            branch.clone(),
6023            Some(BranchNodeMasks {
6024                hash_mask: TrieMask::new(0b01),
6025                tree_mask: TrieMask::default(),
6026            }),
6027            false,
6028        )
6029        .unwrap();
6030
6031        // Reveal a branch node and one of its children
6032        //
6033        // Branch (Mask = 11)
6034        // ├── 0 -> Hash (Path = 0)
6035        // └── 1 -> Leaf (Path = 1)
6036        sparse
6037            .reveal_nodes(&mut [
6038                ProofTrieNode {
6039                    path: Nibbles::default(),
6040                    node: branch,
6041                    masks: Some(BranchNodeMasks {
6042                        hash_mask: TrieMask::default(),
6043                        tree_mask: TrieMask::new(0b01),
6044                    }),
6045                },
6046                ProofTrieNode {
6047                    path: Nibbles::from_nibbles([0x1]),
6048                    node: TrieNode::Leaf(leaf),
6049                    masks: None,
6050                },
6051            ])
6052            .unwrap();
6053
6054        // Removing a blinded leaf should result in an error
6055        assert_matches!(
6056            sparse.remove_leaf(&Nibbles::from_nibbles([0x0]), &provider).map_err(|e| e.into_kind()),
6057            Err(SparseTrieErrorKind::BlindedNode { path, hash }) if path == Nibbles::from_nibbles([0x0]) && hash == B256::repeat_byte(1)
6058        );
6059    }
6060
6061    #[test]
6062    fn sparse_trie_remove_leaf_non_existent() {
6063        let leaf = LeafNode::new(
6064            Nibbles::default(),
6065            alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec(),
6066        );
6067        let branch = TrieNode::Branch(BranchNode::new(
6068            vec![
6069                RlpNode::word_rlp(&B256::repeat_byte(1)),
6070                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(),
6071            ],
6072            TrieMask::new(0b11),
6073        ));
6074
6075        let provider = DefaultTrieNodeProvider;
6076        let mut sparse = ParallelSparseTrie::from_root(
6077            branch.clone(),
6078            Some(BranchNodeMasks {
6079                hash_mask: TrieMask::new(0b01),
6080                tree_mask: TrieMask::default(),
6081            }),
6082            false,
6083        )
6084        .unwrap();
6085
6086        // Reveal a branch node and one of its children
6087        //
6088        // Branch (Mask = 11)
6089        // ├── 0 -> Hash (Path = 0)
6090        // └── 1 -> Leaf (Path = 1)
6091        sparse
6092            .reveal_nodes(&mut [
6093                ProofTrieNode {
6094                    path: Nibbles::default(),
6095                    node: branch,
6096                    masks: Some(BranchNodeMasks {
6097                        hash_mask: TrieMask::default(),
6098                        tree_mask: TrieMask::new(0b01),
6099                    }),
6100                },
6101                ProofTrieNode {
6102                    path: Nibbles::from_nibbles([0x1]),
6103                    node: TrieNode::Leaf(leaf),
6104                    masks: None,
6105                },
6106            ])
6107            .unwrap();
6108
6109        // Removing a non-existent leaf should be a noop
6110        let sparse_old = sparse.clone();
6111        assert_matches!(sparse.remove_leaf(&Nibbles::from_nibbles([0x2]), &provider), Ok(()));
6112        assert_eq!(sparse, sparse_old);
6113    }
6114
6115    #[test]
6116    fn sparse_trie_fuzz() {
6117        // Having only the first 3 nibbles set, we narrow down the range of keys
6118        // to 4096 different hashes. It allows us to generate collisions more likely
6119        // to test the sparse trie updates.
6120        const KEY_NIBBLES_LEN: usize = 3;
6121
6122        fn test(updates: Vec<(BTreeMap<Nibbles, Account>, BTreeSet<Nibbles>)>) {
6123            {
6124                let mut state = BTreeMap::default();
6125                let default_provider = DefaultTrieNodeProvider;
6126                let provider_factory = create_test_provider_factory();
6127                let mut sparse = ParallelSparseTrie::default().with_updates(true);
6128
6129                for (update, keys_to_delete) in updates {
6130                    // Insert state updates into the sparse trie and calculate the root
6131                    for (key, account) in update.clone() {
6132                        let account = account.into_trie_account(EMPTY_ROOT_HASH);
6133                        let mut account_rlp = Vec::new();
6134                        account.encode(&mut account_rlp);
6135                        sparse.update_leaf(key, account_rlp, &default_provider).unwrap();
6136                    }
6137                    // We need to clone the sparse trie, so that all updated branch nodes are
6138                    // preserved, and not only those that were changed after the last call to
6139                    // `root()`.
6140                    let mut updated_sparse = sparse.clone();
6141                    let sparse_root = updated_sparse.root();
6142                    let sparse_updates = updated_sparse.take_updates();
6143
6144                    // Insert state updates into the hash builder and calculate the root
6145                    state.extend(update);
6146                    let provider = provider_factory.provider().unwrap();
6147                    let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref());
6148                    let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
6149                        run_hash_builder(
6150                            state.clone(),
6151                            trie_cursor.account_trie_cursor().unwrap(),
6152                            Default::default(),
6153                            state.keys().copied(),
6154                        );
6155
6156                    // Extract account nodes before moving hash_builder_updates
6157                    let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone();
6158
6159                    // Write trie updates to the database
6160                    let provider_rw = provider_factory.provider_rw().unwrap();
6161                    provider_rw.write_trie_updates(hash_builder_updates).unwrap();
6162                    provider_rw.commit().unwrap();
6163
6164                    // Assert that the sparse trie root matches the hash builder root
6165                    assert_eq!(sparse_root, hash_builder_root);
6166                    // Assert that the sparse trie updates match the hash builder updates
6167                    pretty_assertions::assert_eq!(
6168                        BTreeMap::from_iter(sparse_updates.updated_nodes),
6169                        BTreeMap::from_iter(hash_builder_account_nodes)
6170                    );
6171                    // Assert that the sparse trie nodes match the hash builder proof nodes
6172                    assert_eq_parallel_sparse_trie_proof_nodes(
6173                        &updated_sparse,
6174                        hash_builder_proof_nodes,
6175                    );
6176
6177                    // Delete some keys from both the hash builder and the sparse trie and check
6178                    // that the sparse trie root still matches the hash builder root
6179                    for key in &keys_to_delete {
6180                        state.remove(key).unwrap();
6181                        sparse.remove_leaf(key, &default_provider).unwrap();
6182                    }
6183
6184                    // We need to clone the sparse trie, so that all updated branch nodes are
6185                    // preserved, and not only those that were changed after the last call to
6186                    // `root()`.
6187                    let mut updated_sparse = sparse.clone();
6188                    let sparse_root = updated_sparse.root();
6189                    let sparse_updates = updated_sparse.take_updates();
6190
6191                    let provider = provider_factory.provider().unwrap();
6192                    let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref());
6193                    let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) =
6194                        run_hash_builder(
6195                            state.clone(),
6196                            trie_cursor.account_trie_cursor().unwrap(),
6197                            keys_to_delete
6198                                .iter()
6199                                .map(|nibbles| B256::from_slice(&nibbles.pack()))
6200                                .collect(),
6201                            state.keys().copied(),
6202                        );
6203
6204                    // Extract account nodes before moving hash_builder_updates
6205                    let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone();
6206
6207                    // Write trie updates to the database
6208                    let provider_rw = provider_factory.provider_rw().unwrap();
6209                    provider_rw.write_trie_updates(hash_builder_updates).unwrap();
6210                    provider_rw.commit().unwrap();
6211
6212                    // Assert that the sparse trie root matches the hash builder root
6213                    assert_eq!(sparse_root, hash_builder_root);
6214                    // Assert that the sparse trie updates match the hash builder updates
6215                    pretty_assertions::assert_eq!(
6216                        BTreeMap::from_iter(sparse_updates.updated_nodes),
6217                        BTreeMap::from_iter(hash_builder_account_nodes)
6218                    );
6219                    // Assert that the sparse trie nodes match the hash builder proof nodes
6220                    assert_eq_parallel_sparse_trie_proof_nodes(
6221                        &updated_sparse,
6222                        hash_builder_proof_nodes,
6223                    );
6224                }
6225            }
6226        }
6227
6228        fn transform_updates(
6229            updates: Vec<BTreeMap<Nibbles, Account>>,
6230            mut rng: impl rand::Rng,
6231        ) -> Vec<(BTreeMap<Nibbles, Account>, BTreeSet<Nibbles>)> {
6232            let mut keys = BTreeSet::new();
6233            updates
6234                .into_iter()
6235                .map(|update| {
6236                    keys.extend(update.keys().copied());
6237
6238                    let keys_to_delete_len = update.len() / 2;
6239                    let keys_to_delete = (0..keys_to_delete_len)
6240                        .map(|_| {
6241                            let key =
6242                                *rand::seq::IteratorRandom::choose(keys.iter(), &mut rng).unwrap();
6243                            keys.take(&key).unwrap()
6244                        })
6245                        .collect();
6246
6247                    (update, keys_to_delete)
6248                })
6249                .collect::<Vec<_>>()
6250        }
6251
6252        proptest!(ProptestConfig::with_cases(10), |(
6253            updates in proptest::collection::vec(
6254                proptest::collection::btree_map(
6255                    any_with::<Nibbles>(SizeRange::new(KEY_NIBBLES_LEN..=KEY_NIBBLES_LEN)).prop_map(pad_nibbles_right),
6256                    arb::<Account>(),
6257                    1..50,
6258                ),
6259                1..50,
6260            ).prop_perturb(transform_updates)
6261        )| {
6262            test(updates)
6263        });
6264    }
6265
6266    #[test]
6267    fn sparse_trie_two_leaves_at_lower_roots() {
6268        let provider = DefaultTrieNodeProvider;
6269        let mut trie = ParallelSparseTrie::default().with_updates(true);
6270        let key_50 = Nibbles::unpack(hex!(
6271            "0x5000000000000000000000000000000000000000000000000000000000000000"
6272        ));
6273        let key_51 = Nibbles::unpack(hex!(
6274            "0x5100000000000000000000000000000000000000000000000000000000000000"
6275        ));
6276
6277        let account = Account::default().into_trie_account(EMPTY_ROOT_HASH);
6278        let mut account_rlp = Vec::new();
6279        account.encode(&mut account_rlp);
6280
6281        // Add a leaf and calculate the root.
6282        trie.update_leaf(key_50, account_rlp.clone(), &provider).unwrap();
6283        trie.root();
6284
6285        // Add a second leaf and assert that the root is the expected value.
6286        trie.update_leaf(key_51, account_rlp.clone(), &provider).unwrap();
6287
6288        let expected_root =
6289            hex!("0xdaf0ef9f91a2f179bb74501209effdb5301db1697bcab041eca2234b126e25de");
6290        let root = trie.root();
6291        assert_eq!(root, expected_root);
6292        assert_eq!(SparseTrieUpdates::default(), trie.take_updates());
6293    }
6294
6295    /// We have three leaves that share the same prefix: 0x00, 0x01 and 0x02. Hash builder trie has
6296    /// only nodes 0x00 and 0x01, and we have proofs for them. Node B is new and inserted in the
6297    /// sparse trie first.
6298    ///
6299    /// 1. Reveal the hash builder proof to leaf 0x00 in the sparse trie.
6300    /// 2. Insert leaf 0x01 into the sparse trie.
6301    /// 3. Reveal the hash builder proof to leaf 0x02 in the sparse trie.
6302    ///
6303    /// The hash builder proof to the leaf 0x02 didn't have the leaf 0x01 at the corresponding
6304    /// nibble of the branch node, so we need to adjust the branch node instead of fully
6305    /// replacing it.
6306    #[test]
6307    fn sparse_trie_reveal_node_1() {
6308        let key1 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x00]));
6309        let key2 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x01]));
6310        let key3 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x02]));
6311        let value = || Account::default();
6312        let value_encoded = || {
6313            let mut account_rlp = Vec::new();
6314            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
6315            account_rlp
6316        };
6317
6318        // Generate the proof for the root node and initialize the sparse trie with it
6319        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6320            run_hash_builder(
6321                [(key1(), value()), (key3(), value())],
6322                NoopAccountTrieCursor::default(),
6323                Default::default(),
6324                [Nibbles::default()],
6325            );
6326
6327        let provider = DefaultTrieNodeProvider;
6328        let masks = match (
6329            branch_node_hash_masks.get(&Nibbles::default()).copied(),
6330            branch_node_tree_masks.get(&Nibbles::default()).copied(),
6331        ) {
6332            (Some(h), Some(t)) => Some(BranchNodeMasks { hash_mask: h, tree_mask: t }),
6333            (Some(h), None) => {
6334                Some(BranchNodeMasks { hash_mask: h, tree_mask: TrieMask::default() })
6335            }
6336            (None, Some(t)) => {
6337                Some(BranchNodeMasks { hash_mask: TrieMask::default(), tree_mask: t })
6338            }
6339            (None, None) => None,
6340        };
6341        let mut sparse = ParallelSparseTrie::from_root(
6342            TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(),
6343            masks,
6344            false,
6345        )
6346        .unwrap();
6347
6348        // Generate the proof for the first key and reveal it in the sparse trie
6349        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6350            run_hash_builder(
6351                [(key1(), value()), (key3(), value())],
6352                NoopAccountTrieCursor::default(),
6353                Default::default(),
6354                [key1()],
6355            );
6356        let mut revealed_nodes: Vec<ProofTrieNode> = hash_builder_proof_nodes
6357            .nodes_sorted()
6358            .into_iter()
6359            .map(|(path, node)| {
6360                let hash_mask = branch_node_hash_masks.get(&path).copied();
6361                let tree_mask = branch_node_tree_masks.get(&path).copied();
6362                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
6363                ProofTrieNode { path, node: TrieNode::decode(&mut &node[..]).unwrap(), masks }
6364            })
6365            .collect();
6366        sparse.reveal_nodes(&mut revealed_nodes).unwrap();
6367
6368        // Check that the branch node exists with only two nibbles set
6369        assert_eq!(
6370            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6371            Some(&SparseNode::new_branch(0b101.into()))
6372        );
6373
6374        // Insert the leaf for the second key
6375        sparse.update_leaf(key2(), value_encoded(), &provider).unwrap();
6376
6377        // Check that the branch node was updated and another nibble was set
6378        assert_eq!(
6379            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6380            Some(&SparseNode::new_branch(0b111.into()))
6381        );
6382
6383        // Generate the proof for the third key and reveal it in the sparse trie
6384        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6385            run_hash_builder(
6386                [(key1(), value()), (key3(), value())],
6387                NoopAccountTrieCursor::default(),
6388                Default::default(),
6389                [key3()],
6390            );
6391        let mut revealed_nodes: Vec<ProofTrieNode> = hash_builder_proof_nodes
6392            .nodes_sorted()
6393            .into_iter()
6394            .map(|(path, node)| {
6395                let hash_mask = branch_node_hash_masks.get(&path).copied();
6396                let tree_mask = branch_node_tree_masks.get(&path).copied();
6397                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
6398                ProofTrieNode { path, node: TrieNode::decode(&mut &node[..]).unwrap(), masks }
6399            })
6400            .collect();
6401        sparse.reveal_nodes(&mut revealed_nodes).unwrap();
6402
6403        // Check that nothing changed in the branch node
6404        assert_eq!(
6405            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6406            Some(&SparseNode::new_branch(0b111.into()))
6407        );
6408
6409        // Generate the nodes for the full trie with all three key using the hash builder, and
6410        // compare them to the sparse trie
6411        let (_, _, hash_builder_proof_nodes, _, _) = run_hash_builder(
6412            [(key1(), value()), (key2(), value()), (key3(), value())],
6413            NoopAccountTrieCursor::default(),
6414            Default::default(),
6415            [key1(), key2(), key3()],
6416        );
6417
6418        assert_eq_parallel_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes);
6419    }
6420
6421    /// We have three leaves: 0x0000, 0x0101, and 0x0102. Hash builder trie has all nodes, and we
6422    /// have proofs for them.
6423    ///
6424    /// 1. Reveal the hash builder proof to leaf 0x00 in the sparse trie.
6425    /// 2. Remove leaf 0x00 from the sparse trie (that will remove the branch node and create an
6426    ///    extension node with the key 0x0000).
6427    /// 3. Reveal the hash builder proof to leaf 0x0101 in the sparse trie.
6428    ///
6429    /// The hash builder proof to the leaf 0x0101 had a branch node in the path, but we turned it
6430    /// into an extension node, so it should ignore this node.
6431    #[test]
6432    fn sparse_trie_reveal_node_2() {
6433        let key1 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x00, 0x00]));
6434        let key2 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x01, 0x01]));
6435        let key3 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x01, 0x02]));
6436        let value = || Account::default();
6437
6438        // Generate the proof for the root node and initialize the sparse trie with it
6439        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6440            run_hash_builder(
6441                [(key1(), value()), (key2(), value()), (key3(), value())],
6442                NoopAccountTrieCursor::default(),
6443                Default::default(),
6444                [Nibbles::default()],
6445            );
6446
6447        let provider = DefaultTrieNodeProvider;
6448        let masks = match (
6449            branch_node_hash_masks.get(&Nibbles::default()).copied(),
6450            branch_node_tree_masks.get(&Nibbles::default()).copied(),
6451        ) {
6452            (Some(h), Some(t)) => Some(BranchNodeMasks { hash_mask: h, tree_mask: t }),
6453            (Some(h), None) => {
6454                Some(BranchNodeMasks { hash_mask: h, tree_mask: TrieMask::default() })
6455            }
6456            (None, Some(t)) => {
6457                Some(BranchNodeMasks { hash_mask: TrieMask::default(), tree_mask: t })
6458            }
6459            (None, None) => None,
6460        };
6461        let mut sparse = ParallelSparseTrie::from_root(
6462            TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(),
6463            masks,
6464            false,
6465        )
6466        .unwrap();
6467
6468        // Generate the proof for the children of the root branch node and reveal it in the sparse
6469        // trie
6470        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6471            run_hash_builder(
6472                [(key1(), value()), (key2(), value()), (key3(), value())],
6473                NoopAccountTrieCursor::default(),
6474                Default::default(),
6475                [key1(), Nibbles::from_nibbles_unchecked([0x01])],
6476            );
6477        let mut revealed_nodes: Vec<ProofTrieNode> = hash_builder_proof_nodes
6478            .nodes_sorted()
6479            .into_iter()
6480            .map(|(path, node)| {
6481                let hash_mask = branch_node_hash_masks.get(&path).copied();
6482                let tree_mask = branch_node_tree_masks.get(&path).copied();
6483                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
6484                ProofTrieNode { path, node: TrieNode::decode(&mut &node[..]).unwrap(), masks }
6485            })
6486            .collect();
6487        sparse.reveal_nodes(&mut revealed_nodes).unwrap();
6488
6489        // Check that the branch node exists
6490        assert_eq!(
6491            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6492            Some(&SparseNode::new_branch(0b11.into()))
6493        );
6494
6495        // Remove the leaf for the first key
6496        sparse.remove_leaf(&key1(), &provider).unwrap();
6497
6498        // Check that the branch node was turned into an extension node
6499        assert_eq!(
6500            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6501            Some(&SparseNode::new_ext(Nibbles::from_nibbles_unchecked([0x01])))
6502        );
6503
6504        // Generate the proof for the third key and reveal it in the sparse trie
6505        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6506            run_hash_builder(
6507                [(key1(), value()), (key2(), value()), (key3(), value())],
6508                NoopAccountTrieCursor::default(),
6509                Default::default(),
6510                [key2()],
6511            );
6512        let mut revealed_nodes: Vec<ProofTrieNode> = hash_builder_proof_nodes
6513            .nodes_sorted()
6514            .into_iter()
6515            .map(|(path, node)| {
6516                let hash_mask = branch_node_hash_masks.get(&path).copied();
6517                let tree_mask = branch_node_tree_masks.get(&path).copied();
6518                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
6519                ProofTrieNode { path, node: TrieNode::decode(&mut &node[..]).unwrap(), masks }
6520            })
6521            .collect();
6522        sparse.reveal_nodes(&mut revealed_nodes).unwrap();
6523
6524        // Check that nothing changed in the extension node
6525        assert_eq!(
6526            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6527            Some(&SparseNode::new_ext(Nibbles::from_nibbles_unchecked([0x01])))
6528        );
6529    }
6530
6531    /// We have two leaves that share the same prefix: 0x0001 and 0x0002, and a leaf with a
6532    /// different prefix: 0x0100. Hash builder trie has only the first two leaves, and we have
6533    /// proofs for them.
6534    ///
6535    /// 1. Insert the leaf 0x0100 into the sparse trie, and check that the root extension node was
6536    ///    turned into a branch node.
6537    /// 2. Reveal the leaf 0x0001 in the sparse trie, and check that the root branch node wasn't
6538    ///    overwritten with the extension node from the proof.
6539    #[test]
6540    fn sparse_trie_reveal_node_3() {
6541        let key1 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x00, 0x01]));
6542        let key2 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x00, 0x02]));
6543        let key3 = || pad_nibbles_right(Nibbles::from_nibbles_unchecked([0x01, 0x00]));
6544        let value = || Account::default();
6545        let value_encoded = || {
6546            let mut account_rlp = Vec::new();
6547            value().into_trie_account(EMPTY_ROOT_HASH).encode(&mut account_rlp);
6548            account_rlp
6549        };
6550
6551        // Generate the proof for the root node and initialize the sparse trie with it
6552        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6553            run_hash_builder(
6554                [(key1(), value()), (key2(), value())],
6555                NoopAccountTrieCursor::default(),
6556                Default::default(),
6557                [Nibbles::default()],
6558            );
6559
6560        let provider = DefaultTrieNodeProvider;
6561        let masks = match (
6562            branch_node_hash_masks.get(&Nibbles::default()).copied(),
6563            branch_node_tree_masks.get(&Nibbles::default()).copied(),
6564        ) {
6565            (Some(h), Some(t)) => Some(BranchNodeMasks { hash_mask: h, tree_mask: t }),
6566            (Some(h), None) => {
6567                Some(BranchNodeMasks { hash_mask: h, tree_mask: TrieMask::default() })
6568            }
6569            (None, Some(t)) => {
6570                Some(BranchNodeMasks { hash_mask: TrieMask::default(), tree_mask: t })
6571            }
6572            (None, None) => None,
6573        };
6574        let mut sparse = ParallelSparseTrie::from_root(
6575            TrieNode::decode(&mut &hash_builder_proof_nodes.nodes_sorted()[0].1[..]).unwrap(),
6576            masks,
6577            false,
6578        )
6579        .unwrap();
6580
6581        // Check that the root extension node exists
6582        assert_matches!(
6583            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6584            Some(SparseNode::Extension { key, hash: None, store_in_db_trie: None }) if *key == Nibbles::from_nibbles([0x00])
6585        );
6586
6587        // Insert the leaf with a different prefix
6588        sparse.update_leaf(key3(), value_encoded(), &provider).unwrap();
6589
6590        // Check that the extension node was turned into a branch node
6591        assert_matches!(
6592            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6593            Some(SparseNode::Branch { state_mask, hash: None, store_in_db_trie: None }) if *state_mask == TrieMask::new(0b11)
6594        );
6595
6596        // Generate the proof for the first key and reveal it in the sparse trie
6597        let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) =
6598            run_hash_builder(
6599                [(key1(), value()), (key2(), value())],
6600                NoopAccountTrieCursor::default(),
6601                Default::default(),
6602                [key1()],
6603            );
6604        let mut revealed_nodes: Vec<ProofTrieNode> = hash_builder_proof_nodes
6605            .nodes_sorted()
6606            .into_iter()
6607            .map(|(path, node)| {
6608                let hash_mask = branch_node_hash_masks.get(&path).copied();
6609                let tree_mask = branch_node_tree_masks.get(&path).copied();
6610                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
6611                ProofTrieNode { path, node: TrieNode::decode(&mut &node[..]).unwrap(), masks }
6612            })
6613            .collect();
6614        sparse.reveal_nodes(&mut revealed_nodes).unwrap();
6615
6616        // Check that the branch node wasn't overwritten by the extension node in the proof
6617        assert_matches!(
6618            sparse.upper_subtrie.nodes.get(&Nibbles::default()),
6619            Some(SparseNode::Branch { state_mask, hash: None, store_in_db_trie: None }) if *state_mask == TrieMask::new(0b11)
6620        );
6621    }
6622
6623    #[test]
6624    fn test_update_leaf_cross_level() {
6625        let ctx = ParallelSparseTrieTestContext;
6626        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6627
6628        // Test adding leaves that demonstrate the cross-level behavior
6629        // Based on the example: leaves 0x1234, 0x1245, 0x1334, 0x1345
6630        //
6631        // Final trie structure:
6632        // Upper trie:
6633        //   0x: Extension { key: 0x1 }
6634        //   └── 0x1: Branch { state_mask: 0x1100 }
6635        //       └── Subtrie (0x12): pointer to lower subtrie
6636        //       └── Subtrie (0x13): pointer to lower subtrie
6637        //
6638        // Lower subtrie (0x12):
6639        //   0x12: Branch { state_mask: 0x8 | 0x10 }
6640        //   ├── 0x123: Leaf { key: 0x4 }
6641        //   └── 0x124: Leaf { key: 0x5 }
6642        //
6643        // Lower subtrie (0x13):
6644        //   0x13: Branch { state_mask: 0x8 | 0x10 }
6645        //   ├── 0x133: Leaf { key: 0x4 }
6646        //   └── 0x134: Leaf { key: 0x5 }
6647
6648        // First add leaf 0x1345 - this should create a leaf in upper trie at 0x
6649        let (leaf1_path, value1) = ctx.create_test_leaf([0x1, 0x3, 0x4, 0x5], 1);
6650        trie.update_leaf(leaf1_path, value1.clone(), DefaultTrieNodeProvider).unwrap();
6651
6652        // Verify upper trie has a leaf at the root with key 1345
6653        ctx.assert_upper_subtrie(&trie)
6654            .has_leaf(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x3, 0x4, 0x5]))
6655            .has_value(&leaf1_path, &value1);
6656
6657        // Add leaf 0x1234 - this should go first in the upper subtrie
6658        let (leaf2_path, value2) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 2);
6659        trie.update_leaf(leaf2_path, value2.clone(), DefaultTrieNodeProvider).unwrap();
6660
6661        // Upper trie should now have a branch at 0x1
6662        ctx.assert_upper_subtrie(&trie)
6663            .has_branch(&Nibbles::from_nibbles([0x1]), &[0x2, 0x3])
6664            .has_no_value(&leaf1_path)
6665            .has_no_value(&leaf2_path);
6666
6667        // Add leaf 0x1245 - this should cause a branch and create the 0x12 subtrie
6668        let (leaf3_path, value3) = ctx.create_test_leaf([0x1, 0x2, 0x4, 0x5], 3);
6669        trie.update_leaf(leaf3_path, value3.clone(), DefaultTrieNodeProvider).unwrap();
6670
6671        // Verify lower subtrie at 0x12 exists with correct structure
6672        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6673            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x3, 0x4])
6674            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &Nibbles::from_nibbles([0x4]))
6675            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x4]), &Nibbles::from_nibbles([0x5]))
6676            .has_value(&leaf2_path, &value2)
6677            .has_value(&leaf3_path, &value3);
6678
6679        // Add leaf 0x1334 - this should create another lower subtrie
6680        let (leaf4_path, value4) = ctx.create_test_leaf([0x1, 0x3, 0x3, 0x4], 4);
6681        trie.update_leaf(leaf4_path, value4.clone(), DefaultTrieNodeProvider).unwrap();
6682
6683        // Verify lower subtrie at 0x13 exists with correct values
6684        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x3]))
6685            .has_value(&leaf1_path, &value1)
6686            .has_value(&leaf4_path, &value4);
6687
6688        // Verify the 0x12 subtrie still has its values
6689        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6690            .has_value(&leaf2_path, &value2)
6691            .has_value(&leaf3_path, &value3);
6692
6693        // Upper trie has no values
6694        ctx.assert_upper_subtrie(&trie)
6695            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1]))
6696            .has_branch(&Nibbles::from_nibbles([0x1]), &[0x2, 0x3])
6697            .has_no_value(&leaf1_path)
6698            .has_no_value(&leaf2_path)
6699            .has_no_value(&leaf3_path)
6700            .has_no_value(&leaf4_path);
6701    }
6702
6703    #[test]
6704    fn test_update_leaf_split_at_level_boundary() {
6705        let ctx = ParallelSparseTrieTestContext;
6706        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6707
6708        // This test demonstrates what happens when we insert leaves that cause
6709        // splitting exactly at the upper/lower trie boundary (2 nibbles).
6710        //
6711        // Final trie structure:
6712        // Upper trie:
6713        //   0x: Extension { key: 0x12 }
6714        //       └── Subtrie (0x12): pointer to lower subtrie
6715        //
6716        // Lower subtrie (0x12):
6717        //   0x12: Branch { state_mask: 0x4 | 0x8 }
6718        //   ├── 0x122: Leaf { key: 0x4 }
6719        //   └── 0x123: Leaf { key: 0x4 }
6720
6721        // First insert a leaf that ends exactly at the boundary (2 nibbles)
6722        let (first_leaf_path, first_value) = ctx.create_test_leaf([0x1, 0x2, 0x2, 0x4], 1);
6723
6724        trie.update_leaf(first_leaf_path, first_value.clone(), DefaultTrieNodeProvider).unwrap();
6725
6726        // In an empty trie, the first leaf becomes the root, regardless of path length
6727        ctx.assert_upper_subtrie(&trie)
6728            .has_leaf(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x2, 0x4]))
6729            .has_value(&first_leaf_path, &first_value);
6730
6731        // Now insert another leaf that shares the same 2-nibble prefix
6732        let (second_leaf_path, second_value) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 2);
6733
6734        trie.update_leaf(second_leaf_path, second_value.clone(), DefaultTrieNodeProvider).unwrap();
6735
6736        // Now both leaves should be in a lower subtrie at index [0x1, 0x2]
6737        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6738            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x2, 0x3])
6739            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x2]), &Nibbles::from_nibbles([0x4]))
6740            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &Nibbles::from_nibbles([0x4]))
6741            .has_value(&first_leaf_path, &first_value)
6742            .has_value(&second_leaf_path, &second_value);
6743
6744        // Upper subtrie should no longer have these values
6745        ctx.assert_upper_subtrie(&trie)
6746            .has_no_value(&first_leaf_path)
6747            .has_no_value(&second_leaf_path);
6748    }
6749
6750    #[test]
6751    fn test_update_subtrie_with_multiple_leaves() {
6752        let ctx = ParallelSparseTrieTestContext;
6753        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6754
6755        // First, add multiple leaves that will create a subtrie structure
6756        // All leaves share the prefix [0x1, 0x2] to ensure they create a subtrie
6757        //
6758        // This should result in a trie with the following structure:
6759        // 0x: Extension { key: 0x12 }
6760        //  └── Subtrie (0x12):
6761        //      0x12: Branch { state_mask: 0x3 | 0x4 }
6762        //      ├── 0x123: Branch { state_mask: 0x4 | 0x5 }
6763        //      │   ├── 0x1234: Leaf { key: 0x }
6764        //      │   └── 0x1235: Leaf { key: 0x }
6765        //      └── 0x124: Branch { state_mask: 0x6 | 0x7 }
6766        //          ├── 0x1246: Leaf { key: 0x }
6767        //          └── 0x1247: Leaf { key: 0x }
6768        let leaves = ctx.create_test_leaves(&[
6769            &[0x1, 0x2, 0x3, 0x4],
6770            &[0x1, 0x2, 0x3, 0x5],
6771            &[0x1, 0x2, 0x4, 0x6],
6772            &[0x1, 0x2, 0x4, 0x7],
6773        ]);
6774
6775        // Insert all leaves
6776        ctx.update_leaves(&mut trie, leaves.clone());
6777
6778        // Verify the upper subtrie has an extension node at the root with key 0x12
6779        ctx.assert_upper_subtrie(&trie)
6780            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2]));
6781
6782        // Verify the subtrie structure using fluent assertions
6783        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6784            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x3, 0x4])
6785            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5])
6786            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x4]), &[0x6, 0x7])
6787            .has_value(&leaves[0].0, &leaves[0].1)
6788            .has_value(&leaves[1].0, &leaves[1].1)
6789            .has_value(&leaves[2].0, &leaves[2].1)
6790            .has_value(&leaves[3].0, &leaves[3].1);
6791
6792        // Now update one of the leaves with a new value
6793        let updated_path = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]);
6794        let (_, updated_value) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 100);
6795
6796        trie.update_leaf(updated_path, updated_value.clone(), DefaultTrieNodeProvider).unwrap();
6797
6798        // Verify the subtrie structure is maintained and value is updated
6799        // The branch structure should remain the same and all values should be present
6800        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6801            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x3, 0x4])
6802            .has_value(&updated_path, &updated_value)
6803            .has_value(&leaves[1].0, &leaves[1].1)
6804            .has_value(&leaves[2].0, &leaves[2].1)
6805            .has_value(&leaves[3].0, &leaves[3].1);
6806
6807        // Add a new leaf that extends an existing branch
6808        let (new_leaf_path, new_leaf_value) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x6], 200);
6809
6810        trie.update_leaf(new_leaf_path, new_leaf_value.clone(), DefaultTrieNodeProvider).unwrap();
6811
6812        // Verify the branch at [0x1, 0x2, 0x3] now has an additional child
6813        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6814            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5, 0x6])
6815            .has_value(&new_leaf_path, &new_leaf_value);
6816    }
6817
6818    #[test]
6819    fn test_update_subtrie_extension_node_subtrie() {
6820        let ctx = ParallelSparseTrieTestContext;
6821        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6822
6823        // All leaves share the prefix [0x1, 0x2] to ensure they create a subtrie
6824        //
6825        // This should result in a trie with the following structure
6826        // 0x: Extension { key: 0x123 }
6827        //  └── Subtrie (0x12):
6828        //      0x123: Branch { state_mask: 0x3 | 0x4 }
6829        //      ├── 0x123: Leaf { key: 0x4 }
6830        //      └── 0x124: Leaf { key: 0x5 }
6831        let leaves = ctx.create_test_leaves(&[&[0x1, 0x2, 0x3, 0x4], &[0x1, 0x2, 0x3, 0x5]]);
6832
6833        // Insert all leaves
6834        ctx.update_leaves(&mut trie, leaves.clone());
6835
6836        // Verify the upper subtrie has an extension node at the root with key 0x123
6837        ctx.assert_upper_subtrie(&trie)
6838            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x3]));
6839
6840        // Verify the lower subtrie structure
6841        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6842            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5])
6843            .has_value(&leaves[0].0, &leaves[0].1)
6844            .has_value(&leaves[1].0, &leaves[1].1);
6845    }
6846
6847    #[test]
6848    fn update_subtrie_extension_node_cross_level() {
6849        let ctx = ParallelSparseTrieTestContext;
6850        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6851
6852        // First, add multiple leaves that will create a subtrie structure
6853        // All leaves share the prefix [0x1, 0x2] to ensure they create a branch node and subtrie
6854        //
6855        // This should result in a trie with the following structure
6856        // 0x: Extension { key: 0x12 }
6857        //  └── Subtrie (0x12):
6858        //      0x12: Branch { state_mask: 0x3 | 0x4 }
6859        //      ├── 0x123: Leaf { key: 0x4 }
6860        //      └── 0x124: Leaf { key: 0x5 }
6861        let leaves = ctx.create_test_leaves(&[&[0x1, 0x2, 0x3, 0x4], &[0x1, 0x2, 0x4, 0x5]]);
6862
6863        // Insert all leaves
6864        ctx.update_leaves(&mut trie, leaves.clone());
6865
6866        // Verify the upper subtrie has an extension node at the root with key 0x12
6867        ctx.assert_upper_subtrie(&trie)
6868            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2]));
6869
6870        // Verify the lower subtrie structure
6871        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
6872            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x3, 0x4])
6873            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &Nibbles::from_nibbles([0x4]))
6874            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x4]), &Nibbles::from_nibbles([0x5]))
6875            .has_value(&leaves[0].0, &leaves[0].1)
6876            .has_value(&leaves[1].0, &leaves[1].1);
6877    }
6878
6879    #[test]
6880    fn test_update_single_nibble_paths() {
6881        let ctx = ParallelSparseTrieTestContext;
6882        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6883
6884        // Test edge case: single nibble paths that create branches in upper trie
6885        //
6886        // Final trie structure:
6887        // Upper trie:
6888        //   0x: Branch { state_mask: 0x1 | 0x2 | 0x4 | 0x8 }
6889        //   ├── 0x0: Leaf { key: 0x }
6890        //   ├── 0x1: Leaf { key: 0x }
6891        //   ├── 0x2: Leaf { key: 0x }
6892        //   └── 0x3: Leaf { key: 0x }
6893
6894        // Insert leaves with single nibble paths
6895        let (leaf1_path, value1) = ctx.create_test_leaf([0x0], 1);
6896        let (leaf2_path, value2) = ctx.create_test_leaf([0x1], 2);
6897        let (leaf3_path, value3) = ctx.create_test_leaf([0x2], 3);
6898        let (leaf4_path, value4) = ctx.create_test_leaf([0x3], 4);
6899
6900        ctx.update_leaves(
6901            &mut trie,
6902            [
6903                (leaf1_path, value1.clone()),
6904                (leaf2_path, value2.clone()),
6905                (leaf3_path, value3.clone()),
6906                (leaf4_path, value4.clone()),
6907            ],
6908        );
6909
6910        // Verify upper trie has a branch at root with 4 children
6911        ctx.assert_upper_subtrie(&trie)
6912            .has_branch(&Nibbles::default(), &[0x0, 0x1, 0x2, 0x3])
6913            .has_leaf(&Nibbles::from_nibbles([0x0]), &Nibbles::default())
6914            .has_leaf(&Nibbles::from_nibbles([0x1]), &Nibbles::default())
6915            .has_leaf(&Nibbles::from_nibbles([0x2]), &Nibbles::default())
6916            .has_leaf(&Nibbles::from_nibbles([0x3]), &Nibbles::default())
6917            .has_value(&leaf1_path, &value1)
6918            .has_value(&leaf2_path, &value2)
6919            .has_value(&leaf3_path, &value3)
6920            .has_value(&leaf4_path, &value4);
6921    }
6922
6923    #[test]
6924    fn test_update_deep_extension_chain() {
6925        let ctx = ParallelSparseTrieTestContext;
6926        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6927
6928        // Test edge case: deep extension chains that span multiple levels
6929        //
6930        // Final trie structure:
6931        // Upper trie:
6932        //   0x: Extension { key: 0x111111 }
6933        //       └── Subtrie (0x11): pointer to lower subtrie
6934        //
6935        // Lower subtrie (0x11):
6936        //   0x111111: Branch { state_mask: 0x1 | 0x2 }
6937        //   ├── 0x1111110: Leaf { key: 0x }
6938        //   └── 0x1111111: Leaf { key: 0x }
6939
6940        // Create leaves with a long common prefix
6941        let (leaf1_path, value1) = ctx.create_test_leaf([0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0], 1);
6942        let (leaf2_path, value2) = ctx.create_test_leaf([0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1], 2);
6943
6944        ctx.update_leaves(&mut trie, [(leaf1_path, value1.clone()), (leaf2_path, value2.clone())]);
6945
6946        // Verify upper trie has extension with the full common prefix
6947        ctx.assert_upper_subtrie(&trie).has_extension(
6948            &Nibbles::default(),
6949            &Nibbles::from_nibbles([0x1, 0x1, 0x1, 0x1, 0x1, 0x1]),
6950        );
6951
6952        // Verify lower subtrie has branch structure
6953        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x1]))
6954            .has_branch(&Nibbles::from_nibbles([0x1, 0x1, 0x1, 0x1, 0x1, 0x1]), &[0x0, 0x1])
6955            .has_leaf(
6956                &Nibbles::from_nibbles([0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0]),
6957                &Nibbles::default(),
6958            )
6959            .has_leaf(
6960                &Nibbles::from_nibbles([0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1]),
6961                &Nibbles::default(),
6962            )
6963            .has_value(&leaf1_path, &value1)
6964            .has_value(&leaf2_path, &value2);
6965    }
6966
6967    #[test]
6968    fn test_update_branch_with_all_nibbles() {
6969        let ctx = ParallelSparseTrieTestContext;
6970        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
6971
6972        // Test edge case: branch node with all 16 possible nibble children
6973        //
6974        // Final trie structure:
6975        // Upper trie:
6976        //   0x: Extension { key: 0xA }
6977        //       └── Subtrie (0xA0): pointer to lower subtrie
6978        //
6979        // Lower subtrie (0xA0):
6980        //   0xA0: Branch { state_mask: 0xFFFF } (all 16 children)
6981        //   ├── 0xA00: Leaf { key: 0x }
6982        //   ├── 0xA01: Leaf { key: 0x }
6983        //   ├── 0xA02: Leaf { key: 0x }
6984        //   ... (all nibbles 0x0 through 0xF)
6985        //   └── 0xA0F: Leaf { key: 0x }
6986
6987        // Create leaves for all 16 possible nibbles
6988        let mut leaves = Vec::new();
6989        for nibble in 0x0..=0xF {
6990            let (path, value) = ctx.create_test_leaf([0xA, 0x0, nibble], nibble as u64 + 1);
6991            leaves.push((path, value));
6992        }
6993
6994        // Insert all leaves
6995        ctx.update_leaves(&mut trie, leaves.iter().cloned());
6996
6997        // Verify upper trie structure
6998        ctx.assert_upper_subtrie(&trie)
6999            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0xA, 0x0]));
7000
7001        // Verify lower subtrie has branch with all 16 children
7002        let mut subtrie_assert =
7003            ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xA, 0x0])).has_branch(
7004                &Nibbles::from_nibbles([0xA, 0x0]),
7005                &[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF],
7006            );
7007
7008        // Verify all leaves exist
7009        for (i, (path, value)) in leaves.iter().enumerate() {
7010            subtrie_assert = subtrie_assert
7011                .has_leaf(&Nibbles::from_nibbles([0xA, 0x0, i as u8]), &Nibbles::default())
7012                .has_value(path, value);
7013        }
7014    }
7015
7016    #[test]
7017    fn test_update_creates_multiple_subtries() {
7018        let ctx = ParallelSparseTrieTestContext;
7019        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7020
7021        // Test edge case: updates that create multiple subtries at once
7022        //
7023        // Final trie structure:
7024        // Upper trie:
7025        //   0x: Extension { key: 0x0 }
7026        //       └── 0x0: Branch { state_mask: 0xF }
7027        //           ├── Subtrie (0x00): pointer
7028        //           ├── Subtrie (0x01): pointer
7029        //           ├── Subtrie (0x02): pointer
7030        //           └── Subtrie (0x03): pointer
7031        //
7032        // Each lower subtrie has leaves:
7033        //   0xXY: Leaf { key: 0xZ... }
7034
7035        // Create leaves that will force multiple subtries
7036        let leaves = [
7037            ctx.create_test_leaf([0x0, 0x0, 0x1, 0x2], 1),
7038            ctx.create_test_leaf([0x0, 0x1, 0x3, 0x4], 2),
7039            ctx.create_test_leaf([0x0, 0x2, 0x5, 0x6], 3),
7040            ctx.create_test_leaf([0x0, 0x3, 0x7, 0x8], 4),
7041        ];
7042
7043        // Insert all leaves
7044        ctx.update_leaves(&mut trie, leaves.iter().cloned());
7045
7046        // Verify upper trie has extension then branch
7047        ctx.assert_upper_subtrie(&trie)
7048            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x0]))
7049            .has_branch(&Nibbles::from_nibbles([0x0]), &[0x0, 0x1, 0x2, 0x3]);
7050
7051        // Verify each subtrie exists and contains its leaf
7052        for (i, (leaf_path, leaf_value)) in leaves.iter().enumerate() {
7053            let subtrie_path = Nibbles::from_nibbles([0x0, i as u8]);
7054            ctx.assert_subtrie(&trie, subtrie_path)
7055                .has_leaf(
7056                    &subtrie_path,
7057                    &Nibbles::from_nibbles(match i {
7058                        0 => vec![0x1, 0x2],
7059                        1 => vec![0x3, 0x4],
7060                        2 => vec![0x5, 0x6],
7061                        3 => vec![0x7, 0x8],
7062                        _ => unreachable!(),
7063                    }),
7064                )
7065                .has_value(leaf_path, leaf_value);
7066        }
7067    }
7068
7069    #[test]
7070    fn test_update_extension_to_branch_transformation() {
7071        let ctx = ParallelSparseTrieTestContext;
7072        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7073
7074        // Test edge case: extension node transforms to branch when split
7075        //
7076        // Initial state after first two leaves:
7077        // Upper trie:
7078        //   0x: Extension { key: 0xFF0 }
7079        //       └── Subtrie (0xFF): pointer
7080        //
7081        // After third leaf (0xF0...):
7082        // Upper trie:
7083        //   0x: Extension { key: 0xF }
7084        //       └── 0xF: Branch { state_mask: 0x10 | 0x8000 }
7085        //           ├── Subtrie (0xF0): pointer
7086        //           └── Subtrie (0xFF): pointer
7087
7088        // First two leaves share prefix 0xFF0
7089        let (leaf1_path, value1) = ctx.create_test_leaf([0xF, 0xF, 0x0, 0x1], 1);
7090        let (leaf2_path, value2) = ctx.create_test_leaf([0xF, 0xF, 0x0, 0x2], 2);
7091        let (leaf3_path, value3) = ctx.create_test_leaf([0xF, 0x0, 0x0, 0x3], 3);
7092
7093        ctx.update_leaves(&mut trie, [(leaf1_path, value1.clone()), (leaf2_path, value2.clone())]);
7094
7095        // Verify initial extension structure
7096        ctx.assert_upper_subtrie(&trie)
7097            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0xF, 0xF, 0x0]));
7098
7099        // Add leaf that splits the extension
7100        ctx.update_leaves(&mut trie, [(leaf3_path, value3.clone())]);
7101
7102        // Verify transformed structure
7103        ctx.assert_upper_subtrie(&trie)
7104            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0xF]))
7105            .has_branch(&Nibbles::from_nibbles([0xF]), &[0x0, 0xF]);
7106
7107        // Verify subtries
7108        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xF, 0xF]))
7109            .has_branch(&Nibbles::from_nibbles([0xF, 0xF, 0x0]), &[0x1, 0x2])
7110            .has_leaf(&Nibbles::from_nibbles([0xF, 0xF, 0x0, 0x1]), &Nibbles::default())
7111            .has_leaf(&Nibbles::from_nibbles([0xF, 0xF, 0x0, 0x2]), &Nibbles::default())
7112            .has_value(&leaf1_path, &value1)
7113            .has_value(&leaf2_path, &value2);
7114
7115        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xF, 0x0]))
7116            .has_leaf(&Nibbles::from_nibbles([0xF, 0x0]), &Nibbles::from_nibbles([0x0, 0x3]))
7117            .has_value(&leaf3_path, &value3);
7118    }
7119
7120    #[test]
7121    fn test_update_upper_extension_reveal_lower_hash_node() {
7122        let ctx = ParallelSparseTrieTestContext;
7123
7124        // Test edge case: extension pointing to hash node that gets updated to branch
7125        // and reveals the hash node from lower trie
7126        //
7127        // Setup:
7128        // Upper trie:
7129        //   0x: Extension { key: 0xAB }
7130        //       └── Subtrie (0xAB): pointer
7131        // Lower trie (0xAB):
7132        //   0xAB: Hash
7133        //
7134        // After update:
7135        // Upper trie:
7136        //   0x: Extension { key: 0xA }
7137        //       └── 0xA: Branch { state_mask: 0b100000000001 }
7138        //                ├── 0xA0: Leaf { value: ... }
7139        //                └── 0xAB: pointer
7140        // Lower trie (0xAB):
7141        //   0xAB: Branch { state_mask: 0b11 }
7142        //         ├── 0xAB1: Hash
7143        //         └── 0xAB2: Hash
7144
7145        // Create a mock provider that will provide the hash node
7146        let mut provider = MockTrieNodeProvider::new();
7147
7148        // Create revealed branch which will get revealed and add it to the mock provider
7149        let child_hashes = [
7150            RlpNode::word_rlp(&B256::repeat_byte(0x11)),
7151            RlpNode::word_rlp(&B256::repeat_byte(0x22)),
7152        ];
7153        let revealed_branch = create_branch_node_with_children(&[0x1, 0x2], child_hashes);
7154        let mut encoded = Vec::new();
7155        revealed_branch.encode(&mut encoded);
7156        provider.add_revealed_node(
7157            Nibbles::from_nibbles([0xA, 0xB]),
7158            RevealedNode { node: encoded.into(), tree_mask: None, hash_mask: None },
7159        );
7160
7161        let mut trie = new_test_trie(
7162            [
7163                (Nibbles::default(), SparseNode::new_ext(Nibbles::from_nibbles([0xA, 0xB]))),
7164                (Nibbles::from_nibbles([0xA, 0xB]), SparseNode::Hash(B256::repeat_byte(0x42))),
7165            ]
7166            .into_iter(),
7167        );
7168
7169        // Now add a leaf that will force the hash node to become a branch
7170        let (leaf_path, value) = ctx.create_test_leaf([0xA, 0x0], 1);
7171        trie.update_leaf(leaf_path, value, provider).unwrap();
7172
7173        // Verify the structure: extension should now terminate in a branch on the upper trie
7174        ctx.assert_upper_subtrie(&trie)
7175            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0xA]))
7176            .has_branch(&Nibbles::from_nibbles([0xA]), &[0x0, 0xB]);
7177
7178        // Verify the lower trie now has a branch structure
7179        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xA, 0xB]))
7180            .has_branch(&Nibbles::from_nibbles([0xA, 0xB]), &[0x1, 0x2])
7181            .has_hash(&Nibbles::from_nibbles([0xA, 0xB, 0x1]), &B256::repeat_byte(0x11))
7182            .has_hash(&Nibbles::from_nibbles([0xA, 0xB, 0x2]), &B256::repeat_byte(0x22));
7183    }
7184
7185    #[test]
7186    fn test_update_long_shared_prefix_at_boundary() {
7187        let ctx = ParallelSparseTrieTestContext;
7188        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7189
7190        // Test edge case: leaves with long shared prefix that ends exactly at 2-nibble boundary
7191        //
7192        // Final trie structure:
7193        // Upper trie:
7194        //   0x: Extension { key: 0xAB }
7195        //       └── Subtrie (0xAB): pointer to lower subtrie
7196        //
7197        // Lower subtrie (0xAB):
7198        //   0xAB: Branch { state_mask: 0x1000 | 0x2000 }
7199        //   ├── 0xABC: Leaf { key: 0xDEF }
7200        //   └── 0xABD: Leaf { key: 0xEF0 }
7201
7202        // Create leaves that share exactly 2 nibbles
7203        let (leaf1_path, value1) = ctx.create_test_leaf([0xA, 0xB, 0xC, 0xD, 0xE, 0xF], 1);
7204        let (leaf2_path, value2) = ctx.create_test_leaf([0xA, 0xB, 0xD, 0xE, 0xF, 0x0], 2);
7205
7206        trie.update_leaf(leaf1_path, value1.clone(), DefaultTrieNodeProvider).unwrap();
7207        trie.update_leaf(leaf2_path, value2.clone(), DefaultTrieNodeProvider).unwrap();
7208
7209        // Verify upper trie structure
7210        ctx.assert_upper_subtrie(&trie)
7211            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0xA, 0xB]));
7212
7213        // Verify lower subtrie structure
7214        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xA, 0xB]))
7215            .has_branch(&Nibbles::from_nibbles([0xA, 0xB]), &[0xC, 0xD])
7216            .has_leaf(
7217                &Nibbles::from_nibbles([0xA, 0xB, 0xC]),
7218                &Nibbles::from_nibbles([0xD, 0xE, 0xF]),
7219            )
7220            .has_leaf(
7221                &Nibbles::from_nibbles([0xA, 0xB, 0xD]),
7222                &Nibbles::from_nibbles([0xE, 0xF, 0x0]),
7223            )
7224            .has_value(&leaf1_path, &value1)
7225            .has_value(&leaf2_path, &value2);
7226    }
7227
7228    #[test]
7229    fn test_update_branch_to_extension_collapse() {
7230        let ctx = ParallelSparseTrieTestContext;
7231        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7232
7233        // Test creating a trie with leaves that share a long common prefix
7234        //
7235        // Initial state with 3 leaves (0x1234, 0x2345, 0x2356):
7236        // Upper trie:
7237        //   0x: Branch { state_mask: 0x6 }
7238        //       ├── 0x1: Leaf { key: 0x234 }
7239        //       └── 0x2: Extension { key: 0x3 }
7240        //           └── Subtrie (0x23): pointer
7241        // Lower subtrie (0x23):
7242        //   0x23: Branch { state_mask: 0x30 }
7243        //       ├── 0x234: Leaf { key: 0x5 }
7244        //       └── 0x235: Leaf { key: 0x6 }
7245        //
7246        // Then we create a new trie with leaves (0x1234, 0x1235, 0x1236):
7247        // Expected structure:
7248        // Upper trie:
7249        //   0x: Extension { key: 0x123 }
7250        //       └── Subtrie (0x12): pointer
7251        // Lower subtrie (0x12):
7252        //   0x123: Branch { state_mask: 0x70 } // bits 4, 5, 6 set
7253        //       ├── 0x1234: Leaf { key: 0x }
7254        //       ├── 0x1235: Leaf { key: 0x }
7255        //       └── 0x1236: Leaf { key: 0x }
7256
7257        // Create initial leaves
7258        let (leaf1_path, value1) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 1);
7259        let (leaf2_path, value2) = ctx.create_test_leaf([0x2, 0x3, 0x4, 0x5], 2);
7260        let (leaf3_path, value3) = ctx.create_test_leaf([0x2, 0x3, 0x5, 0x6], 3);
7261
7262        trie.update_leaf(leaf1_path, value1, DefaultTrieNodeProvider).unwrap();
7263        trie.update_leaf(leaf2_path, value2, DefaultTrieNodeProvider).unwrap();
7264        trie.update_leaf(leaf3_path, value3, DefaultTrieNodeProvider).unwrap();
7265
7266        // Verify initial structure has branch at root
7267        ctx.assert_upper_subtrie(&trie).has_branch(&Nibbles::default(), &[0x1, 0x2]);
7268
7269        // Now update to create a pattern where extension is more efficient
7270        // Replace leaves to all share prefix 0x123
7271        let (new_leaf1_path, new_value1) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 10);
7272        let (new_leaf2_path, new_value2) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x5], 11);
7273        let (new_leaf3_path, new_value3) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x6], 12);
7274
7275        // Clear and add new leaves
7276        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7277        trie.update_leaf(new_leaf1_path, new_value1.clone(), DefaultTrieNodeProvider).unwrap();
7278        trie.update_leaf(new_leaf2_path, new_value2.clone(), DefaultTrieNodeProvider).unwrap();
7279        trie.update_leaf(new_leaf3_path, new_value3.clone(), DefaultTrieNodeProvider).unwrap();
7280
7281        // Verify new structure has extension
7282        ctx.assert_upper_subtrie(&trie)
7283            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x3]));
7284
7285        // Verify lower subtrie path was correctly updated to 0x123
7286        ctx.assert_subtrie_path(&trie, [0x1, 0x2], [0x1, 0x2, 0x3]);
7287
7288        // Verify lower subtrie - all three leaves should be properly inserted
7289        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
7290            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5, 0x6]) // All three children
7291            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]), &Nibbles::default())
7292            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x5]), &Nibbles::default())
7293            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x6]), &Nibbles::default())
7294            .has_value(&new_leaf1_path, &new_value1)
7295            .has_value(&new_leaf2_path, &new_value2)
7296            .has_value(&new_leaf3_path, &new_value3);
7297    }
7298
7299    #[test]
7300    fn test_update_shared_prefix_patterns() {
7301        let ctx = ParallelSparseTrieTestContext;
7302        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7303
7304        // Test edge case: different patterns of shared prefixes
7305        //
7306        // Final trie structure:
7307        // Upper trie:
7308        //   0x: Branch { state_mask: 0x6 }
7309        //       ├── 0x1: Leaf { key: 0x234 }
7310        //       └── 0x2: Extension { key: 0x3 }
7311        //           └── Subtrie (0x23): pointer
7312        //
7313        // Lower subtrie (0x23):
7314        //   0x23: Branch { state_mask: 0x10 | 0x20 }
7315        //   ├── 0x234: Leaf { key: 0x5 }
7316        //   └── 0x235: Leaf { key: 0x6 }
7317
7318        // Create leaves with different shared prefix patterns
7319        let (leaf1_path, value1) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4], 1);
7320        let (leaf2_path, value2) = ctx.create_test_leaf([0x2, 0x3, 0x4, 0x5], 2);
7321        let (leaf3_path, value3) = ctx.create_test_leaf([0x2, 0x3, 0x5, 0x6], 3);
7322
7323        trie.update_leaf(leaf1_path, value1, DefaultTrieNodeProvider).unwrap();
7324        trie.update_leaf(leaf2_path, value2.clone(), DefaultTrieNodeProvider).unwrap();
7325        trie.update_leaf(leaf3_path, value3.clone(), DefaultTrieNodeProvider).unwrap();
7326
7327        // Verify upper trie structure
7328        ctx.assert_upper_subtrie(&trie)
7329            .has_branch(&Nibbles::default(), &[0x1, 0x2])
7330            .has_leaf(&Nibbles::from_nibbles([0x1]), &Nibbles::from_nibbles([0x2, 0x3, 0x4]))
7331            .has_extension(&Nibbles::from_nibbles([0x2]), &Nibbles::from_nibbles([0x3]));
7332
7333        // Verify lower subtrie structure
7334        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x2, 0x3]))
7335            .has_branch(&Nibbles::from_nibbles([0x2, 0x3]), &[0x4, 0x5])
7336            .has_leaf(&Nibbles::from_nibbles([0x2, 0x3, 0x4]), &Nibbles::from_nibbles([0x5]))
7337            .has_leaf(&Nibbles::from_nibbles([0x2, 0x3, 0x5]), &Nibbles::from_nibbles([0x6]))
7338            .has_value(&leaf2_path, &value2)
7339            .has_value(&leaf3_path, &value3);
7340    }
7341
7342    #[test]
7343    fn test_progressive_branch_creation() {
7344        let ctx = ParallelSparseTrieTestContext;
7345        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7346
7347        // Test starting with a single leaf and progressively adding leaves
7348        // that create branch nodes at shorter and shorter paths
7349        //
7350        // Step 1: Add leaf at 0x12345
7351        // Upper trie:
7352        //   0x: Leaf { key: 0x12345 }
7353        //
7354        // Step 2: Add leaf at 0x12346
7355        // Upper trie:
7356        //   0x: Extension { key: 0x1234 }
7357        //       └── Subtrie (0x12): pointer
7358        // Lower subtrie (0x12):
7359        //   0x1234: Branch { state_mask: 0x60 }  // bits 5 and 6 set
7360        //       ├── 0x12345: Leaf { key: 0x }
7361        //       └── 0x12346: Leaf { key: 0x }
7362        //
7363        // Step 3: Add leaf at 0x1235
7364        // Lower subtrie (0x12) updates to:
7365        //   0x123: Branch { state_mask: 0x30 }  // bits 4 and 5 set
7366        //       ├── 0x1234: Branch { state_mask: 0x60 }
7367        //       │   ├── 0x12345: Leaf { key: 0x }
7368        //       │   └── 0x12346: Leaf { key: 0x }
7369        //       └── 0x1235: Leaf { key: 0x }
7370        //
7371        // Step 4: Add leaf at 0x124
7372        // Lower subtrie (0x12) updates to:
7373        //   0x12: Branch { state_mask: 0x18 }  // bits 3 and 4 set
7374        //       ├── 0x123: Branch { state_mask: 0x30 }
7375        //       │   ├── 0x1234: Branch { state_mask: 0x60 }
7376        //       │   │   ├── 0x12345: Leaf { key: 0x }
7377        //       │   │   └── 0x12346: Leaf { key: 0x }
7378        //       │   └── 0x1235: Leaf { key: 0x }
7379        //       └── 0x124: Leaf { key: 0x }
7380
7381        // Step 1: Add first leaf - initially stored as leaf in upper trie
7382        let (leaf1_path, value1) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4, 0x5], 1);
7383        trie.update_leaf(leaf1_path, value1.clone(), DefaultTrieNodeProvider).unwrap();
7384
7385        // Verify leaf node in upper trie (optimized single-leaf case)
7386        ctx.assert_upper_subtrie(&trie)
7387            .has_leaf(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5]))
7388            .has_value(&leaf1_path, &value1);
7389
7390        // Step 2: Add leaf at 0x12346 - creates branch at 0x1234
7391        let (leaf2_path, value2) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x4, 0x6], 2);
7392        trie.update_leaf(leaf2_path, value2.clone(), DefaultTrieNodeProvider).unwrap();
7393
7394        // Verify extension now goes to 0x1234
7395        ctx.assert_upper_subtrie(&trie)
7396            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]));
7397
7398        // Verify subtrie path updated to 0x1234
7399        ctx.assert_subtrie_path(&trie, [0x1, 0x2], [0x1, 0x2, 0x3, 0x4]);
7400
7401        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
7402            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]), &[0x5, 0x6])
7403            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x5]), &Nibbles::default())
7404            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4, 0x6]), &Nibbles::default())
7405            .has_value(&leaf1_path, &value1)
7406            .has_value(&leaf2_path, &value2);
7407
7408        // Step 3: Add leaf at 0x1235 - creates branch at 0x123
7409        let (leaf3_path, value3) = ctx.create_test_leaf([0x1, 0x2, 0x3, 0x5], 3);
7410        trie.update_leaf(leaf3_path, value3.clone(), DefaultTrieNodeProvider).unwrap();
7411
7412        // Verify extension now goes to 0x123
7413        ctx.assert_upper_subtrie(&trie)
7414            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2, 0x3]));
7415
7416        // Verify subtrie path updated to 0x123
7417        ctx.assert_subtrie_path(&trie, [0x1, 0x2], [0x1, 0x2, 0x3]);
7418
7419        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
7420            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5])
7421            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]), &[0x5, 0x6])
7422            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x5]), &Nibbles::default())
7423            .has_value(&leaf1_path, &value1)
7424            .has_value(&leaf2_path, &value2)
7425            .has_value(&leaf3_path, &value3);
7426
7427        // Step 4: Add leaf at 0x124 - creates branch at 0x12 (subtrie root)
7428        let (leaf4_path, value4) = ctx.create_test_leaf([0x1, 0x2, 0x4], 4);
7429        trie.update_leaf(leaf4_path, value4.clone(), DefaultTrieNodeProvider).unwrap();
7430
7431        // Verify extension now goes to 0x12
7432        ctx.assert_upper_subtrie(&trie)
7433            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles([0x1, 0x2]));
7434
7435        // Verify subtrie path updated to 0x12
7436        ctx.assert_subtrie_path(&trie, [0x1, 0x2], [0x1, 0x2]);
7437
7438        // Verify final structure
7439        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0x1, 0x2]))
7440            .has_branch(&Nibbles::from_nibbles([0x1, 0x2]), &[0x3, 0x4])
7441            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3]), &[0x4, 0x5])
7442            .has_branch(&Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]), &[0x5, 0x6])
7443            .has_leaf(&Nibbles::from_nibbles([0x1, 0x2, 0x4]), &Nibbles::default())
7444            .has_value(&leaf1_path, &value1)
7445            .has_value(&leaf2_path, &value2)
7446            .has_value(&leaf3_path, &value3)
7447            .has_value(&leaf4_path, &value4);
7448    }
7449
7450    #[test]
7451    fn test_update_max_depth_paths() {
7452        let ctx = ParallelSparseTrieTestContext;
7453        let mut trie = ParallelSparseTrie::from_root(TrieNode::EmptyRoot, None, true).unwrap();
7454
7455        // Test edge case: very long paths (64 nibbles - max for addresses/storage)
7456        //
7457        // Final trie structure:
7458        // Upper trie:
7459        //   0x: Extension { key: 0xFF }
7460        //       └── Subtrie (0xFF): pointer
7461        //
7462        // Lower subtrie (0xFF):
7463        //   Has very long paths with slight differences at the end
7464
7465        // Create two 64-nibble paths that differ only in the last nibble
7466        let mut path1_nibbles = vec![0xF; 63];
7467        path1_nibbles.push(0x0);
7468        let mut path2_nibbles = vec![0xF; 63];
7469        path2_nibbles.push(0x1);
7470
7471        let (leaf1_path, value1) = ctx.create_test_leaf(&path1_nibbles, 1);
7472        let (leaf2_path, value2) = ctx.create_test_leaf(&path2_nibbles, 2);
7473
7474        trie.update_leaf(leaf1_path, value1.clone(), DefaultTrieNodeProvider).unwrap();
7475        trie.update_leaf(leaf2_path, value2.clone(), DefaultTrieNodeProvider).unwrap();
7476
7477        // The common prefix of 63 F's will create a very long extension
7478        let extension_key = vec![0xF; 63];
7479        ctx.assert_upper_subtrie(&trie)
7480            .has_extension(&Nibbles::default(), &Nibbles::from_nibbles(&extension_key));
7481
7482        // Verify the subtrie has the branch at the end
7483        ctx.assert_subtrie(&trie, Nibbles::from_nibbles([0xF, 0xF]))
7484            .has_branch(&Nibbles::from_nibbles(&path1_nibbles[..63]), &[0x0, 0x1])
7485            .has_value(&leaf1_path, &value1)
7486            .has_value(&leaf2_path, &value2);
7487    }
7488
7489    #[test]
7490    fn test_hoodie_block_1_data() {
7491        // Reveal node at path Nibbles(0x) - root branch node
7492        let root_branch_stack = vec![
7493            hex!("a0550b6aba4dd4582a2434d2cbdad8d3007d09f622d7a6e6eaa7a49385823c2fa2"),
7494            hex!("a04788a4975a9e1efd29b834fd80fdfe8a57cc1b1c5ace6d30ce5a36a15e0092b3"),
7495            hex!("a093aeccf87da304e6f7d09edc5d7bd3a552808866d2149dd0940507a8f9bfa910"),
7496            hex!("a08b5b423ba68d0dec2eca1f408076f9170678505eb4a5db2abbbd83bb37666949"),
7497            hex!("a08592f62216af4218098a78acad7cf472a727fb55e6c27d3cfdf2774d4518eb83"),
7498            hex!("a0ef02aeee845cb64c11f85edc1a3094227c26445952554b8a9248915d80c746c3"),
7499            hex!("a0df2529ee3a1ce4df5a758cf17e6a86d0fb5ea22ab7071cf60af6412e9b0a428a"),
7500            hex!("a0acaa1092db69cd5a63676685827b3484c4b80dc1d3361f6073bbb9240101e144"),
7501            hex!("a09c3f2bb2a729d71f246a833353ade65667716bb330e0127a3299a42d11200f93"),
7502            hex!("a0ce978470f4c0b1f8069570563a14d2b79d709add2db4bf22dd9b6aed3271c566"),
7503            hex!("a095f783cd1d464a60e3c8adcadc28c6eb9fec7306664df39553be41dccc909606"),
7504            hex!("a0a9083f5fb914b255e1feb5d951a4dfddacf3c8003ef1d1ec6a13bb6ba5b2ac62"),
7505            hex!("a0fec113d537d8577cd361e0cabf5e95ef58f1cc34318292fdecce9fae57c3e094"),
7506            hex!("a08b7465f5fe8b3e3c0d087cb7521310d4065ef2a0ee43bf73f68dee8a5742b3dd"),
7507            hex!("a0c589aa1ae3d5fd87d8640957f7d5184a4ac06f393b453a8e8ed7e8fba0d385c8"),
7508            hex!("a0b516d6f3352f87beab4ed6e7322f191fc7a147686500ef4de7dd290ad784ef51"),
7509        ];
7510
7511        let root_branch_rlp_stack: Vec<RlpNode> = root_branch_stack
7512            .iter()
7513            .map(|hex_str| RlpNode::from_raw_rlp(&hex_str[..]).unwrap())
7514            .collect();
7515
7516        let root_branch_node = BranchNode::new(
7517            root_branch_rlp_stack,
7518            TrieMask::new(0b1111111111111111), // state_mask: all 16 children present
7519        );
7520
7521        let root_branch_masks = Some(BranchNodeMasks {
7522            hash_mask: TrieMask::new(0b1111111111111111),
7523            tree_mask: TrieMask::new(0b1111111111111111),
7524        });
7525
7526        let mut trie = ParallelSparseTrie::from_root(
7527            TrieNode::Branch(root_branch_node),
7528            root_branch_masks,
7529            true,
7530        )
7531        .unwrap();
7532
7533        // Reveal node at path Nibbles(0x3) - branch node
7534        let branch_0x3_stack = vec![
7535            hex!("a09da7d9755fe0c558b3c3de9fdcdf9f28ae641f38c9787b05b73ab22ae53af3e2"),
7536            hex!("a0d9990bf0b810d1145ecb2b011fd68c63cc85564e6724166fd4a9520180706e5f"),
7537            hex!("a0f60eb4b12132a40df05d9bbdb88bbde0185a3f097f3c76bf4200c23eda26cf86"),
7538            hex!("a0ca976997ddaf06f18992f6207e4f6a05979d07acead96568058789017cc6d06b"),
7539            hex!("a04d78166b48044fdc28ed22d2fd39c8df6f8aaa04cb71d3a17286856f6893ff83"),
7540            hex!("a021d4f90c34d3f1706e78463b6482bca77a3aa1cd059a3f326c42a1cfd30b9b60"),
7541            hex!("a0fc3b71c33e2e6b77c5e494c1db7fdbb447473f003daf378c7a63ba9bf3f0049d"),
7542            hex!("a0e33ed2be194a3d93d343e85642447c93a9d0cfc47a016c2c23d14c083be32a7c"),
7543            hex!("a07b8e7a21c1178d28074f157b50fca85ee25c12568ff8e9706dcbcdacb77bf854"),
7544            hex!("a0973274526811393ea0bf4811ca9077531db00d06b86237a2ecd683f55ba4bcb0"),
7545            hex!("a03a93d726d7487874e51b52d8d534c63aa2a689df18e3b307c0d6cb0a388b00f3"),
7546            hex!("a06aa67101d011d1c22fe739ef83b04b5214a3e2f8e1a2625d8bfdb116b447e86f"),
7547            hex!("a02dd545b33c62d33a183e127a08a4767fba891d9f3b94fc20a2ca02600d6d1fff"),
7548            hex!("a0fe6db87d00f06d53bff8169fa497571ff5af1addfb715b649b4d79dd3e394b04"),
7549            hex!("a0d9240a9d2d5851d05a97ff3305334dfdb0101e1e321fc279d2bb3cad6afa8fc8"),
7550            hex!("a01b69c6ab5173de8a8ec53a6ebba965713a4cc7feb86cb3e230def37c230ca2b2"),
7551        ];
7552
7553        let branch_0x3_rlp_stack: Vec<RlpNode> = branch_0x3_stack
7554            .iter()
7555            .map(|hex_str| RlpNode::from_raw_rlp(&hex_str[..]).unwrap())
7556            .collect();
7557
7558        let branch_0x3_node = BranchNode::new(
7559            branch_0x3_rlp_stack,
7560            TrieMask::new(0b1111111111111111), // state_mask: all 16 children present
7561        );
7562
7563        let branch_0x3_masks = Some(BranchNodeMasks {
7564            hash_mask: TrieMask::new(0b0100010000010101),
7565            tree_mask: TrieMask::new(0b0100000000000000),
7566        });
7567
7568        // Reveal node at path Nibbles(0x37) - leaf node
7569        let leaf_path = Nibbles::from_nibbles([0x3, 0x7]);
7570        let leaf_key = Nibbles::unpack(
7571            &hex!("d65eaa92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e42")[..],
7572        );
7573        let leaf_value = hex!("f8440180a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0f57acd40259872606d76197ef052f3d35588dadf919ee1f0e3cb9b62d3f4b02c").to_vec();
7574
7575        let leaf_node = LeafNode::new(leaf_key, leaf_value);
7576        let leaf_masks = None;
7577
7578        trie.reveal_nodes(&mut [
7579            ProofTrieNode {
7580                path: Nibbles::from_nibbles([0x3]),
7581                node: TrieNode::Branch(branch_0x3_node),
7582                masks: branch_0x3_masks,
7583            },
7584            ProofTrieNode { path: leaf_path, node: TrieNode::Leaf(leaf_node), masks: leaf_masks },
7585        ])
7586        .unwrap();
7587
7588        // Update leaf with its new value
7589        let mut leaf_full_path = leaf_path;
7590        leaf_full_path.extend(&leaf_key);
7591
7592        let leaf_new_value = vec![
7593            248, 68, 1, 128, 160, 224, 163, 152, 169, 122, 160, 155, 102, 53, 41, 0, 47, 28, 205,
7594            190, 199, 5, 215, 108, 202, 22, 138, 70, 196, 178, 193, 208, 18, 96, 95, 63, 238, 160,
7595            245, 122, 205, 64, 37, 152, 114, 96, 109, 118, 25, 126, 240, 82, 243, 211, 85, 136,
7596            218, 223, 145, 158, 225, 240, 227, 203, 155, 98, 211, 244, 176, 44,
7597        ];
7598
7599        trie.update_leaf(leaf_full_path, leaf_new_value.clone(), DefaultTrieNodeProvider).unwrap();
7600
7601        // Sanity checks before calculating the root
7602        assert_eq!(
7603            Some(&leaf_new_value),
7604            trie.lower_subtrie_for_path(&leaf_path).unwrap().inner.values.get(&leaf_full_path)
7605        );
7606        assert!(trie.upper_subtrie.inner.values.is_empty());
7607
7608        // Assert the root hash matches the expected value
7609        let expected_root =
7610            b256!("0x29b07de8376e9ce7b3a69e9b102199869514d3f42590b5abc6f7d48ec9b8665c");
7611        assert_eq!(trie.root(), expected_root);
7612    }
7613
7614    #[test]
7615    fn find_leaf_existing_leaf() {
7616        // Create a simple trie with one leaf
7617        let provider = DefaultTrieNodeProvider;
7618        let mut sparse = ParallelSparseTrie::default();
7619        let path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
7620        let value = b"test_value".to_vec();
7621
7622        sparse.update_leaf(path, value.clone(), &provider).unwrap();
7623
7624        // Check that the leaf exists
7625        let result = sparse.find_leaf(&path, None);
7626        assert_matches!(result, Ok(LeafLookup::Exists));
7627
7628        // Check with expected value matching
7629        let result = sparse.find_leaf(&path, Some(&value));
7630        assert_matches!(result, Ok(LeafLookup::Exists));
7631    }
7632
7633    #[test]
7634    fn find_leaf_value_mismatch() {
7635        // Create a simple trie with one leaf
7636        let provider = DefaultTrieNodeProvider;
7637        let mut sparse = ParallelSparseTrie::default();
7638        let path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
7639        let value = b"test_value".to_vec();
7640        let wrong_value = b"wrong_value".to_vec();
7641
7642        sparse.update_leaf(path, value, &provider).unwrap();
7643
7644        // Check with wrong expected value
7645        let result = sparse.find_leaf(&path, Some(&wrong_value));
7646        assert_matches!(
7647            result,
7648            Err(LeafLookupError::ValueMismatch { path: p, expected: Some(e), actual: _a }) if p == path && e == wrong_value
7649        );
7650    }
7651
7652    #[test]
7653    fn find_leaf_not_found_empty_trie() {
7654        // Empty trie
7655        let sparse = ParallelSparseTrie::default();
7656        let path = Nibbles::from_nibbles([0x1, 0x2, 0x3]);
7657
7658        // Leaf should not exist
7659        let result = sparse.find_leaf(&path, None);
7660        assert_matches!(result, Ok(LeafLookup::NonExistent));
7661    }
7662
7663    #[test]
7664    fn find_leaf_empty_trie() {
7665        let sparse = ParallelSparseTrie::default();
7666        let path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7667
7668        let result = sparse.find_leaf(&path, None);
7669        assert_matches!(result, Ok(LeafLookup::NonExistent));
7670    }
7671
7672    #[test]
7673    fn find_leaf_exists_no_value_check() {
7674        let provider = DefaultTrieNodeProvider;
7675        let mut sparse = ParallelSparseTrie::default();
7676        let path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7677        sparse.update_leaf(path, encode_account_value(0), &provider).unwrap();
7678
7679        let result = sparse.find_leaf(&path, None);
7680        assert_matches!(result, Ok(LeafLookup::Exists));
7681    }
7682
7683    #[test]
7684    fn find_leaf_exists_with_value_check_ok() {
7685        let provider = DefaultTrieNodeProvider;
7686        let mut sparse = ParallelSparseTrie::default();
7687        let path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7688        let value = encode_account_value(0);
7689        sparse.update_leaf(path, value.clone(), &provider).unwrap();
7690
7691        let result = sparse.find_leaf(&path, Some(&value));
7692        assert_matches!(result, Ok(LeafLookup::Exists));
7693    }
7694
7695    #[test]
7696    fn find_leaf_exclusion_branch_divergence() {
7697        let provider = DefaultTrieNodeProvider;
7698        let mut sparse = ParallelSparseTrie::default();
7699        let path1 = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]); // Creates branch at 0x12
7700        let path2 = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x5, 0x6]); // Belongs to same branch
7701        let search_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x7, 0x8]); // Diverges at nibble 7
7702
7703        sparse.update_leaf(path1, encode_account_value(0), &provider).unwrap();
7704        sparse.update_leaf(path2, encode_account_value(1), &provider).unwrap();
7705
7706        let result = sparse.find_leaf(&search_path, None);
7707        assert_matches!(result, Ok(LeafLookup::NonExistent))
7708    }
7709
7710    #[test]
7711    fn find_leaf_exclusion_extension_divergence() {
7712        let provider = DefaultTrieNodeProvider;
7713        let mut sparse = ParallelSparseTrie::default();
7714        // This will create an extension node at root with key 0x12
7715        let path1 = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]);
7716        // This path diverges from the extension key
7717        let search_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x7, 0x8]);
7718
7719        sparse.update_leaf(path1, encode_account_value(0), &provider).unwrap();
7720
7721        let result = sparse.find_leaf(&search_path, None);
7722        assert_matches!(result, Ok(LeafLookup::NonExistent))
7723    }
7724
7725    #[test]
7726    fn find_leaf_exclusion_leaf_divergence() {
7727        let provider = DefaultTrieNodeProvider;
7728        let mut sparse = ParallelSparseTrie::default();
7729        let existing_leaf_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7730        let search_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]);
7731
7732        sparse.update_leaf(existing_leaf_path, encode_account_value(0), &provider).unwrap();
7733
7734        let result = sparse.find_leaf(&search_path, None);
7735        assert_matches!(result, Ok(LeafLookup::NonExistent))
7736    }
7737
7738    #[test]
7739    fn find_leaf_exclusion_path_ends_at_branch() {
7740        let provider = DefaultTrieNodeProvider;
7741        let mut sparse = ParallelSparseTrie::default();
7742        let path1 = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]); // Creates branch at 0x12
7743        let path2 = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x5, 0x6]);
7744        let search_path = Nibbles::from_nibbles_unchecked([0x1, 0x2]); // Path of the branch itself
7745
7746        sparse.update_leaf(path1, encode_account_value(0), &provider).unwrap();
7747        sparse.update_leaf(path2, encode_account_value(1), &provider).unwrap();
7748
7749        let result = sparse.find_leaf(&search_path, None);
7750        assert_matches!(result, Ok(LeafLookup::NonExistent));
7751    }
7752
7753    #[test]
7754    fn find_leaf_error_blinded_node_at_leaf_path() {
7755        // Scenario: The node *at* the leaf path is blinded.
7756        let blinded_hash = B256::repeat_byte(0xBB);
7757        let leaf_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7758
7759        let sparse = new_test_trie(
7760            [
7761                (
7762                    // Ext 0x12
7763                    Nibbles::default(),
7764                    SparseNode::new_ext(Nibbles::from_nibbles_unchecked([0x1, 0x2])),
7765                ),
7766                (
7767                    // Ext 0x123
7768                    Nibbles::from_nibbles_unchecked([0x1, 0x2]),
7769                    SparseNode::new_ext(Nibbles::from_nibbles_unchecked([0x3])),
7770                ),
7771                (
7772                    // Branch at 0x123, child 4
7773                    Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3]),
7774                    SparseNode::new_branch(TrieMask::new(0b10000)),
7775                ),
7776                (
7777                    // Blinded node at 0x1234
7778                    leaf_path,
7779                    SparseNode::Hash(blinded_hash),
7780                ),
7781            ]
7782            .into_iter(),
7783        );
7784
7785        let result = sparse.find_leaf(&leaf_path, None);
7786
7787        // Should error because it hit the blinded node exactly at the leaf path
7788        assert_matches!(result, Err(LeafLookupError::BlindedNode { path, hash })
7789            if path == leaf_path && hash == blinded_hash
7790        );
7791    }
7792
7793    #[test]
7794    fn find_leaf_error_blinded_node() {
7795        let blinded_hash = B256::repeat_byte(0xAA);
7796        let path_to_blind = Nibbles::from_nibbles_unchecked([0x1]);
7797        let search_path = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
7798
7799        let sparse = new_test_trie(
7800            [
7801                // Root is a branch with child 0x1 (blinded) and 0x5 (revealed leaf)
7802                // So we set Bit 1 and Bit 5 in the state_mask
7803                (Nibbles::default(), SparseNode::new_branch(TrieMask::new(0b100010))),
7804                (path_to_blind, SparseNode::Hash(blinded_hash)),
7805                (
7806                    Nibbles::from_nibbles_unchecked([0x5]),
7807                    SparseNode::new_leaf(Nibbles::from_nibbles_unchecked([0x6, 0x7, 0x8])),
7808                ),
7809            ]
7810            .into_iter(),
7811        );
7812
7813        let result = sparse.find_leaf(&search_path, None);
7814
7815        // Should error because it hit the blinded node at path 0x1
7816        assert_matches!(result, Err(LeafLookupError::BlindedNode { path, hash })
7817            if path == path_to_blind && hash == blinded_hash
7818        );
7819    }
7820
7821    #[test]
7822    fn test_mainnet_block_24185431_storage_0x6ba784ee() {
7823        reth_tracing::init_test_tracing();
7824
7825        // Reveal branch at 0x3 with full state
7826        let mut branch_0x3_hashes = vec![
7827            B256::from(hex!("fc11ba8de4b220b8f19a09f0676c69b8e18bae1350788392640069e59b41733d")),
7828            B256::from(hex!("8afe085cc6685680bd8ba4bac6e65937a4babf737dc5e7413d21cdda958e8f74")),
7829            B256::from(hex!("c7b6f7c0fc601a27aece6ec178fd9be17cdee77c4884ecfbe1ee459731eb57da")),
7830            B256::from(hex!("71c1aec60db78a2deb4e10399b979a2ed5be42b4ee0c0a17c614f9ddc9f9072e")),
7831            B256::from(hex!("e9261302e7c0b77930eaf1851b585210906cd01e015ab6be0f7f3c0cc947c32a")),
7832            B256::from(hex!("38ce8f369c56bd77fabdf679b27265b1f8d0a54b09ef612c8ee8ddfc6b3fab95")),
7833            B256::from(hex!("7b507a8936a28c5776b647d1c4bda0bbbb3d0d227f16c5f5ebba58d02e31918d")),
7834            B256::from(hex!("0f456b9457a824a81e0eb555aa861461acb38674dcf36959b3b26deb24ed0af9")),
7835            B256::from(hex!("2145420289652722ad199ba932622e3003c779d694fa5a2acfb2f77b0782b38a")),
7836            B256::from(hex!("2c1a04dce1a9e2f1cfbf8806edce50a356dfa58e7e7c542c848541502613b796")),
7837            B256::from(hex!("dad7ca55186ac8f40d4450dc874166df8267b44abc07e684d9507260f5712df3")),
7838            B256::from(hex!("3a8c2a1d7d2423e92965ec29014634e7f0307ded60b1a63d28c86c3222b24236")),
7839            B256::from(hex!("4e9929e6728b3a7bf0db6a0750ab376045566b556c9c605e606ecb8ec25200d7")),
7840            B256::from(hex!("1797c36f98922f52292c161590057a1b5582d5503e3370bcfbf6fd939f3ec98b")),
7841            B256::from(hex!("9e514589a9c9210b783c19fa3f0b384bbfaefe98f10ea189a2bfc58c6bf000a1")),
7842            B256::from(hex!("85bdaabbcfa583cbd049650e41d3d19356bd833b3ed585cf225a3548557c7fa3")),
7843        ];
7844        let branch_0x3_node = create_branch_node_with_children(
7845            &[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf],
7846            branch_0x3_hashes.iter().map(RlpNode::word_rlp),
7847        );
7848
7849        // Reveal branch at 0x31
7850        let branch_0x31_hashes = vec![B256::from(hex!(
7851            "3ca994ba59ce70b83fee1f01731c8dac4fdd0f70ade79bf9b0695c4c53531aab"
7852        ))];
7853        let branch_0x31_node = create_branch_node_with_children(
7854            &[0xc],
7855            branch_0x31_hashes.into_iter().map(|h| RlpNode::word_rlp(&h)),
7856        );
7857
7858        // Reveal leaf at 0x31b0b645a6c4a0a1bb3d2f0c1d31c39f4aba2e3b015928a8eef7161e28388b81
7859        let leaf_path = hex!("31b0b645a6c4a0a1bb3d2f0c1d31c39f4aba2e3b015928a8eef7161e28388b81");
7860        let leaf_nibbles = Nibbles::unpack(leaf_path.as_slice());
7861        let leaf_value = hex!("0009ae8ce8245bff").to_vec();
7862
7863        // Reveal branch at 0x31c
7864        let branch_0x31c_hashes = vec![
7865            B256::from(hex!("1a68fdb36b77e9332b49a977faf800c22d0199e6cecf44032bb083c78943e540")),
7866            B256::from(hex!("cd4622c6df6fd7172c7fed1b284ef241e0f501b4c77b675ef10c612bd0948a7a")),
7867            B256::from(hex!("abf3603d2f991787e21f1709ee4c7375d85dfc506995c0435839fccf3fe2add4")),
7868        ];
7869        let branch_0x31c_node = create_branch_node_with_children(
7870            &[0x3, 0x7, 0xc],
7871            branch_0x31c_hashes.into_iter().map(|h| RlpNode::word_rlp(&h)),
7872        );
7873        let mut branch_0x31c_node_encoded = Vec::new();
7874        branch_0x31c_node.encode(&mut branch_0x31c_node_encoded);
7875
7876        // Create a mock provider and preload 0x31c onto it, it will be revealed during remove_leaf.
7877        let mut provider = MockTrieNodeProvider::new();
7878        provider.add_revealed_node(
7879            Nibbles::from_nibbles([0x3, 0x1, 0xc]),
7880            RevealedNode {
7881                node: branch_0x31c_node_encoded.into(),
7882                tree_mask: Some(0.into()),
7883                hash_mask: Some(4096.into()),
7884            },
7885        );
7886
7887        // Reveal the trie structure using ProofTrieNode
7888        let mut proof_nodes = vec![
7889            ProofTrieNode {
7890                path: Nibbles::from_nibbles([0x3]),
7891                node: branch_0x3_node,
7892                masks: Some(BranchNodeMasks {
7893                    tree_mask: TrieMask::new(26099),
7894                    hash_mask: TrieMask::new(65535),
7895                }),
7896            },
7897            ProofTrieNode {
7898                path: Nibbles::from_nibbles([0x3, 0x1]),
7899                node: branch_0x31_node,
7900                masks: Some(BranchNodeMasks {
7901                    tree_mask: TrieMask::new(4096),
7902                    hash_mask: TrieMask::new(4096),
7903                }),
7904            },
7905        ];
7906
7907        // Create a sparse trie and reveal nodes
7908        let mut trie = ParallelSparseTrie::default()
7909            .with_root(
7910                TrieNode::Extension(ExtensionNode {
7911                    key: Nibbles::from_nibbles([0x3]),
7912                    child: RlpNode::word_rlp(&B256::ZERO),
7913                }),
7914                None,
7915                true,
7916            )
7917            .expect("root revealed");
7918
7919        trie.reveal_nodes(&mut proof_nodes).unwrap();
7920
7921        // Update the leaf in order to reveal it in the trie
7922        trie.update_leaf(leaf_nibbles, leaf_value, &provider).unwrap();
7923
7924        // Now delete the leaf
7925        trie.remove_leaf(&leaf_nibbles, &provider).unwrap();
7926
7927        // Compute the root to trigger updates
7928        let _ = trie.root();
7929
7930        // Assert the resulting branch node updates
7931        let updates = trie.updates_ref();
7932
7933        // Check that the branch at 0x3 was updated with the expected structure
7934        let branch_0x3_update = updates
7935            .updated_nodes
7936            .get(&Nibbles::from_nibbles([0x3]))
7937            .expect("Branch at 0x3 should be in updates");
7938
7939        // We no longer expect to track the hash for child 1
7940        branch_0x3_hashes.remove(1);
7941
7942        // Expected structure from prompt.md
7943        let expected_branch = BranchNodeCompact::new(
7944            0b1111111111111111,
7945            0b0110010111110011,
7946            0b1111111111111101,
7947            branch_0x3_hashes,
7948            None,
7949        );
7950
7951        assert_eq!(branch_0x3_update, &expected_branch);
7952    }
7953
7954    #[test]
7955    fn test_get_leaf_value_lower_subtrie() {
7956        // This test demonstrates that get_leaf_value must look in the correct subtrie,
7957        // not always in upper_subtrie.
7958
7959        // Set up a root branch pointing to nibble 0x1, and a branch at [0x1] pointing to
7960        // nibble 0x2, so that the lower subtrie at [0x1, 0x2] is reachable.
7961        let root_branch =
7962            create_branch_node_with_children(&[0x1], [RlpNode::word_rlp(&B256::repeat_byte(0xAA))]);
7963        let branch_at_1 =
7964            create_branch_node_with_children(&[0x2], [RlpNode::word_rlp(&B256::repeat_byte(0xBB))]);
7965        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
7966        trie.reveal_nodes(&mut [ProofTrieNode {
7967            path: Nibbles::from_nibbles([0x1]),
7968            node: branch_at_1,
7969            masks: None,
7970        }])
7971        .unwrap();
7972
7973        // Create a leaf node with path >= 2 nibbles (will go to lower subtrie)
7974        let leaf_path = Nibbles::from_nibbles([0x1, 0x2]);
7975        let leaf_key = Nibbles::from_nibbles([0x3, 0x4]);
7976        let leaf_node = create_leaf_node(leaf_key.to_vec(), 42);
7977
7978        // Reveal the leaf node
7979        trie.reveal_nodes(&mut [ProofTrieNode { path: leaf_path, node: leaf_node, masks: None }])
7980            .unwrap();
7981
7982        // The full path is leaf_path + leaf_key
7983        let full_path = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]);
7984
7985        // Verify the value is stored in the lower subtrie, not upper
7986        let idx = path_subtrie_index_unchecked(&leaf_path);
7987        let lower_subtrie = trie.lower_subtries[idx].as_revealed_ref().unwrap();
7988        assert!(
7989            lower_subtrie.inner.values.contains_key(&full_path),
7990            "value should be in lower subtrie"
7991        );
7992        assert!(
7993            !trie.upper_subtrie.inner.values.contains_key(&full_path),
7994            "value should NOT be in upper subtrie"
7995        );
7996
7997        // get_leaf_value should find the value
7998        assert!(
7999            trie.get_leaf_value(&full_path).is_some(),
8000            "get_leaf_value should find the value in lower subtrie"
8001        );
8002    }
8003
8004    /// Test that `get_leaf_value` correctly returns values stored via `update_leaf`
8005    /// when the leaf node ends up in the upper subtrie (depth < 2).
8006    ///
8007    /// This can happen when the trie is sparse and the leaf is inserted at the root level.
8008    /// Previously, `get_leaf_value` only checked the lower subtrie based on the full path,
8009    /// missing values stored in `upper_subtrie.inner.values`.
8010    #[test]
8011    fn test_get_leaf_value_upper_subtrie_via_update_leaf() {
8012        let provider = MockTrieNodeProvider::new();
8013
8014        // Create an empty trie with an empty root
8015        let mut trie = ParallelSparseTrie::default()
8016            .with_root(TrieNode::EmptyRoot, None, false)
8017            .expect("root revealed");
8018
8019        // Create a full 64-nibble path (like a real account hash)
8020        let full_path = pad_nibbles_right(Nibbles::from_nibbles([0x0, 0xA, 0xB, 0xC]));
8021        let value = encode_account_value(42);
8022
8023        // Insert the leaf - since the trie is empty, the leaf node will be created
8024        // at the root level (depth 0), which is in the upper subtrie
8025        trie.update_leaf(full_path, value.clone(), &provider).unwrap();
8026
8027        // Verify the value is stored in upper_subtrie (where update_leaf puts it)
8028        assert!(
8029            trie.upper_subtrie.inner.values.contains_key(&full_path),
8030            "value should be in upper subtrie after update_leaf"
8031        );
8032
8033        // Verify the value can be retrieved via get_leaf_value
8034        // Before the fix, this would return None because get_leaf_value only
8035        // checked the lower subtrie based on the path length
8036        let retrieved = trie.get_leaf_value(&full_path);
8037        assert_eq!(retrieved, Some(&value));
8038    }
8039
8040    /// Test that `get_leaf_value` works for values in both upper and lower subtries.
8041    #[test]
8042    fn test_get_leaf_value_upper_and_lower_subtries() {
8043        let provider = MockTrieNodeProvider::new();
8044
8045        // Create an empty trie
8046        let mut trie = ParallelSparseTrie::default()
8047            .with_root(TrieNode::EmptyRoot, None, false)
8048            .expect("root revealed");
8049
8050        // Insert first leaf - will be at root level (upper subtrie)
8051        let path1 = pad_nibbles_right(Nibbles::from_nibbles([0x0, 0xA]));
8052        let value1 = encode_account_value(1);
8053        trie.update_leaf(path1, value1.clone(), &provider).unwrap();
8054
8055        // Insert second leaf with different prefix - creates a branch
8056        let path2 = pad_nibbles_right(Nibbles::from_nibbles([0x1, 0xB]));
8057        let value2 = encode_account_value(2);
8058        trie.update_leaf(path2, value2.clone(), &provider).unwrap();
8059
8060        // Both values should be retrievable
8061        assert_eq!(trie.get_leaf_value(&path1), Some(&value1));
8062        assert_eq!(trie.get_leaf_value(&path2), Some(&value2));
8063    }
8064
8065    /// Test that `get_leaf_value` works for storage tries which are often very sparse.
8066    #[test]
8067    fn test_get_leaf_value_sparse_storage_trie() {
8068        let provider = MockTrieNodeProvider::new();
8069
8070        // Simulate a storage trie with a single slot
8071        let mut trie = ParallelSparseTrie::default()
8072            .with_root(TrieNode::EmptyRoot, None, false)
8073            .expect("root revealed");
8074
8075        // Single storage slot - leaf will be at root (depth 0)
8076        let slot_path = pad_nibbles_right(Nibbles::from_nibbles([0x2, 0x9]));
8077        let slot_value = alloy_rlp::encode(U256::from(12345));
8078        trie.update_leaf(slot_path, slot_value.clone(), &provider).unwrap();
8079
8080        // Value should be retrievable
8081        assert_eq!(trie.get_leaf_value(&slot_path), Some(&slot_value));
8082    }
8083
8084    #[test]
8085    fn test_prune_empty_suffix_key_regression() {
8086        // Regression test: when a leaf has an empty suffix key (full path == node path),
8087        // the value must be removed when that path becomes a pruned root.
8088        // This catches the bug where is_strict_descendant fails to remove p == pruned_root.
8089
8090        use crate::provider::DefaultTrieNodeProvider;
8091
8092        let provider = DefaultTrieNodeProvider;
8093        let mut parallel = ParallelSparseTrie::default();
8094
8095        // Large value to ensure nodes have hashes (RLP >= 32 bytes)
8096        let value = {
8097            let account = Account {
8098                nonce: 0x123456789abcdef,
8099                balance: U256::from(0x123456789abcdef0123456789abcdef_u128),
8100                ..Default::default()
8101            };
8102            let mut buf = Vec::new();
8103            account.into_trie_account(EMPTY_ROOT_HASH).encode(&mut buf);
8104            buf
8105        };
8106
8107        // Create a trie with multiple leaves to force a branch at root
8108        for i in 0..16u8 {
8109            parallel
8110                .update_leaf(
8111                    Nibbles::from_nibbles([i, 0x1, 0x2, 0x3, 0x4, 0x5]),
8112                    value.clone(),
8113                    &provider,
8114                )
8115                .unwrap();
8116        }
8117
8118        // Compute root to get hashes
8119        let root_before = parallel.root();
8120
8121        // Prune at depth 0: the children of root become pruned roots
8122        parallel.prune(0);
8123
8124        let root_after = parallel.root();
8125        assert_eq!(root_before, root_after, "root hash must be preserved");
8126
8127        // Key assertion: values under pruned paths must be removed
8128        // With the bug, values at pruned_root paths (not strict descendants) would remain
8129        for i in 0..16u8 {
8130            let path = Nibbles::from_nibbles([i, 0x1, 0x2, 0x3, 0x4, 0x5]);
8131            assert!(
8132                parallel.get_leaf_value(&path).is_none(),
8133                "value at {:?} should be removed after prune",
8134                path
8135            );
8136        }
8137    }
8138
8139    #[test]
8140    fn test_prune_at_various_depths() {
8141        // Test depths 0 and 1, which are in the Upper subtrie (no heat tracking).
8142        // Depth 2 is the boundary where Lower subtries start (UPPER_TRIE_MAX_DEPTH=2),
8143        // and with `depth >= max_depth` heat check, hot Lower subtries at depth 2
8144        // are protected from pruning traversal.
8145        for max_depth in [0, 1] {
8146            let provider = DefaultTrieNodeProvider;
8147            let mut trie = ParallelSparseTrie::default();
8148
8149            let value = large_account_value();
8150
8151            for i in 0..4u8 {
8152                for j in 0..4u8 {
8153                    for k in 0..4u8 {
8154                        trie.update_leaf(
8155                            Nibbles::from_nibbles([i, j, k, 0x1, 0x2, 0x3]),
8156                            value.clone(),
8157                            &provider,
8158                        )
8159                        .unwrap();
8160                    }
8161                }
8162            }
8163
8164            let root_before = trie.root();
8165            let nodes_before = trie.size_hint();
8166
8167            // Prune multiple times to allow heat to fully decay.
8168            // Heat starts at 1 and decays by 1 each cycle for unmodified subtries,
8169            // so we need 2 prune cycles: 1→0, then actual prune.
8170            for _ in 0..2 {
8171                trie.prune(max_depth);
8172            }
8173
8174            let root_after = trie.root();
8175            assert_eq!(root_before, root_after, "root hash should be preserved after prune");
8176
8177            let nodes_after = trie.size_hint();
8178            assert!(
8179                nodes_after < nodes_before,
8180                "node count should decrease after prune at depth {max_depth}"
8181            );
8182
8183            if max_depth == 0 {
8184                // Root + 4 hash stubs for children at [0], [1], [2], [3]
8185                assert_eq!(nodes_after, 5, "root + 4 hash stubs after prune(0)");
8186            }
8187        }
8188    }
8189
8190    #[test]
8191    fn test_prune_empty_trie() {
8192        let mut trie = ParallelSparseTrie::default();
8193        trie.prune(2);
8194        let root = trie.root();
8195        assert_eq!(root, EMPTY_ROOT_HASH, "empty trie should have empty root hash");
8196    }
8197
8198    #[test]
8199    fn test_prune_preserves_root_hash() {
8200        let provider = DefaultTrieNodeProvider;
8201        let mut trie = ParallelSparseTrie::default();
8202
8203        let value = large_account_value();
8204
8205        for i in 0..8u8 {
8206            for j in 0..4u8 {
8207                trie.update_leaf(
8208                    Nibbles::from_nibbles([i, j, 0x3, 0x4, 0x5, 0x6]),
8209                    value.clone(),
8210                    &provider,
8211                )
8212                .unwrap();
8213            }
8214        }
8215
8216        let root_before = trie.root();
8217        trie.prune(1);
8218        let root_after = trie.root();
8219        assert_eq!(root_before, root_after, "root hash must be preserved after prune");
8220    }
8221
8222    #[test]
8223    fn test_prune_single_leaf_trie() {
8224        let provider = DefaultTrieNodeProvider;
8225        let mut trie = ParallelSparseTrie::default();
8226
8227        let value = large_account_value();
8228        trie.update_leaf(Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]), value, &provider).unwrap();
8229
8230        let root_before = trie.root();
8231        let nodes_before = trie.size_hint();
8232
8233        trie.prune(0);
8234
8235        let root_after = trie.root();
8236        assert_eq!(root_before, root_after, "root hash should be preserved");
8237        assert_eq!(trie.size_hint(), nodes_before, "single leaf trie should not change");
8238    }
8239
8240    #[test]
8241    fn test_prune_deep_depth_no_effect() {
8242        let provider = DefaultTrieNodeProvider;
8243        let mut trie = ParallelSparseTrie::default();
8244
8245        let value = large_account_value();
8246
8247        for i in 0..4u8 {
8248            trie.update_leaf(Nibbles::from_nibbles([i, 0x2, 0x3, 0x4]), value.clone(), &provider)
8249                .unwrap();
8250        }
8251
8252        trie.root();
8253        let nodes_before = trie.size_hint();
8254
8255        trie.prune(100);
8256
8257        assert_eq!(nodes_before, trie.size_hint(), "deep prune should have no effect");
8258    }
8259
8260    #[test]
8261    fn test_prune_extension_node_depth_semantics() {
8262        let provider = DefaultTrieNodeProvider;
8263        let mut trie = ParallelSparseTrie::default();
8264
8265        let value = large_account_value();
8266
8267        trie.update_leaf(Nibbles::from_nibbles([0, 1, 2, 3, 0, 5, 6, 7]), value.clone(), &provider)
8268            .unwrap();
8269        trie.update_leaf(Nibbles::from_nibbles([0, 1, 2, 3, 1, 5, 6, 7]), value, &provider)
8270            .unwrap();
8271
8272        let root_before = trie.root();
8273        // Prune multiple times to allow heat to fully decay.
8274        // Heat starts at 1 and decays by 1 each cycle for unmodified subtries,
8275        // so we need 2 prune cycles: 1→0, then actual prune.
8276        for _ in 0..2 {
8277            trie.prune(1);
8278        }
8279
8280        assert_eq!(root_before, trie.root(), "root hash should be preserved");
8281        // Root + extension + 2 hash stubs (for the two leaves' parent branches)
8282        assert_eq!(trie.size_hint(), 4, "root + extension + hash stubs after prune(1)");
8283    }
8284
8285    #[test]
8286    fn test_prune_embedded_node_preserved() {
8287        let provider = DefaultTrieNodeProvider;
8288        let mut trie = ParallelSparseTrie::default();
8289
8290        let small_value = vec![0x80];
8291        trie.update_leaf(Nibbles::from_nibbles([0x0]), small_value.clone(), &provider).unwrap();
8292        trie.update_leaf(Nibbles::from_nibbles([0x1]), small_value, &provider).unwrap();
8293
8294        let root_before = trie.root();
8295        let nodes_before = trie.size_hint();
8296
8297        trie.prune(0);
8298
8299        assert_eq!(root_before, trie.root(), "root hash must be preserved");
8300
8301        if trie.size_hint() == nodes_before {
8302            assert!(trie.get_leaf_value(&Nibbles::from_nibbles([0x0])).is_some());
8303            assert!(trie.get_leaf_value(&Nibbles::from_nibbles([0x1])).is_some());
8304        }
8305    }
8306
8307    #[test]
8308    fn test_prune_mixed_embedded_and_hashed() {
8309        let provider = DefaultTrieNodeProvider;
8310        let mut trie = ParallelSparseTrie::default();
8311
8312        let large_value = large_account_value();
8313        let small_value = vec![0x80];
8314
8315        for i in 0..8u8 {
8316            let value = if i < 4 { large_value.clone() } else { small_value.clone() };
8317            trie.update_leaf(Nibbles::from_nibbles([i, 0x1, 0x2, 0x3]), value, &provider).unwrap();
8318        }
8319
8320        let root_before = trie.root();
8321        trie.prune(0);
8322        assert_eq!(root_before, trie.root(), "root hash must be preserved");
8323    }
8324
8325    #[test]
8326    fn test_prune_many_lower_subtries() {
8327        let provider = DefaultTrieNodeProvider;
8328
8329        let large_value = large_account_value();
8330
8331        let mut keys = Vec::new();
8332        for first in 0..16u8 {
8333            for second in 0..16u8 {
8334                keys.push(Nibbles::from_nibbles([first, second, 0x1, 0x2, 0x3, 0x4]));
8335            }
8336        }
8337
8338        let mut trie = ParallelSparseTrie::default();
8339
8340        for key in &keys {
8341            trie.update_leaf(*key, large_value.clone(), &provider).unwrap();
8342        }
8343
8344        let root_before = trie.root();
8345
8346        // Prune multiple times to allow heat to fully decay.
8347        // Heat starts at 1 and decays by 1 each cycle for unmodified subtries.
8348        let mut total_pruned = 0;
8349        for _ in 0..2 {
8350            total_pruned += trie.prune(1);
8351        }
8352
8353        assert!(total_pruned > 0, "should have pruned some nodes");
8354        assert_eq!(root_before, trie.root(), "root hash should be preserved");
8355
8356        for key in &keys {
8357            assert!(trie.get_leaf_value(key).is_none(), "value should be pruned");
8358        }
8359    }
8360
8361    #[test]
8362    fn test_prune_max_depth_overflow() {
8363        // Verify that max_depth > 255 is not truncated (was u8, now usize)
8364        let provider = DefaultTrieNodeProvider;
8365        let mut trie = ParallelSparseTrie::default();
8366
8367        let value = large_account_value();
8368
8369        for i in 0..4u8 {
8370            trie.update_leaf(Nibbles::from_nibbles([i, 0x1, 0x2, 0x3]), value.clone(), &provider)
8371                .unwrap();
8372        }
8373
8374        trie.root();
8375        let nodes_before = trie.size_hint();
8376
8377        // If depth were truncated to u8, 300 would become 44 and might prune something
8378        trie.prune(300);
8379
8380        assert_eq!(
8381            nodes_before,
8382            trie.size_hint(),
8383            "prune(300) should have no effect on a shallow trie"
8384        );
8385    }
8386
8387    #[test]
8388    fn test_prune_fast_path_case2_update_after() {
8389        // Test fast-path Case 2: upper prune root is prefix of lower subtrie.
8390        // After pruning, we should be able to update leaves without panic.
8391        let provider = DefaultTrieNodeProvider;
8392        let mut trie = ParallelSparseTrie::default();
8393
8394        let value = large_account_value();
8395
8396        // Create keys that span into lower subtries (path.len() >= UPPER_TRIE_MAX_DEPTH)
8397        // UPPER_TRIE_MAX_DEPTH is typically 2, so paths of length 3+ go to lower subtries
8398        for first in 0..4u8 {
8399            for second in 0..4u8 {
8400                trie.update_leaf(
8401                    Nibbles::from_nibbles([first, second, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]),
8402                    value.clone(),
8403                    &provider,
8404                )
8405                .unwrap();
8406            }
8407        }
8408
8409        let root_before = trie.root();
8410
8411        // Prune at depth 0 - upper roots become prefixes of lower subtrie paths
8412        trie.prune(0);
8413
8414        let root_after = trie.root();
8415        assert_eq!(root_before, root_after, "root hash should be preserved");
8416
8417        // Now try to update a leaf - this should not panic even though lower subtries
8418        // were replaced with Blind(None)
8419        let new_path = Nibbles::from_nibbles([0x5, 0x5, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]);
8420        trie.update_leaf(new_path, value, &provider).unwrap();
8421
8422        // The trie should still be functional
8423        let _ = trie.root();
8424    }
8425
8426    // update_leaves tests
8427
8428    #[test]
8429    fn test_update_leaves_successful_update() {
8430        use crate::LeafUpdate;
8431        use alloy_primitives::map::B256Map;
8432        use std::cell::RefCell;
8433
8434        let provider = DefaultTrieNodeProvider;
8435        let mut trie = ParallelSparseTrie::default();
8436
8437        // Create a leaf in the trie using a full-length key
8438        let b256_key = B256::with_last_byte(42);
8439        let key = Nibbles::unpack(b256_key);
8440        let value = encode_account_value(1);
8441        trie.update_leaf(key, value, &provider).unwrap();
8442
8443        // Create update map with a new value for the same key
8444        let new_value = encode_account_value(2);
8445
8446        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8447        updates.insert(b256_key, LeafUpdate::Changed(new_value));
8448
8449        let proof_targets = RefCell::new(Vec::new());
8450        trie.update_leaves(&mut updates, |path, min_len| {
8451            proof_targets.borrow_mut().push((path, min_len));
8452        })
8453        .unwrap();
8454
8455        // Update should succeed: map empty, callback not invoked
8456        assert!(updates.is_empty(), "Update map should be empty after successful update");
8457        assert!(
8458            proof_targets.borrow().is_empty(),
8459            "Callback should not be invoked for revealed paths"
8460        );
8461    }
8462
8463    #[test]
8464    fn test_update_leaves_insert_new_leaf() {
8465        use crate::LeafUpdate;
8466        use alloy_primitives::map::B256Map;
8467        use std::cell::RefCell;
8468
8469        let mut trie = ParallelSparseTrie::default();
8470
8471        // Insert a NEW leaf (key doesn't exist yet) via update_leaves
8472        let b256_key = B256::with_last_byte(99);
8473        let new_value = encode_account_value(42);
8474
8475        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8476        updates.insert(b256_key, LeafUpdate::Changed(new_value.clone()));
8477
8478        let proof_targets = RefCell::new(Vec::new());
8479        trie.update_leaves(&mut updates, |path, min_len| {
8480            proof_targets.borrow_mut().push((path, min_len));
8481        })
8482        .unwrap();
8483
8484        // Insert should succeed: map empty, callback not invoked
8485        assert!(updates.is_empty(), "Update map should be empty after successful insert");
8486        assert!(
8487            proof_targets.borrow().is_empty(),
8488            "Callback should not be invoked for new leaf insert"
8489        );
8490
8491        // Verify the leaf was actually inserted
8492        let full_path = Nibbles::unpack(b256_key);
8493        assert_eq!(
8494            trie.get_leaf_value(&full_path),
8495            Some(&new_value),
8496            "New leaf value should be retrievable"
8497        );
8498    }
8499
8500    #[test]
8501    fn test_update_leaves_blinded_node() {
8502        use crate::LeafUpdate;
8503        use alloy_primitives::map::B256Map;
8504        use std::cell::RefCell;
8505
8506        // Create a trie with a blinded node
8507        // Use a small value that fits in RLP encoding
8508        let small_value = alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec();
8509        let leaf = LeafNode::new(
8510            Nibbles::default(), // short key for RLP encoding
8511            small_value,
8512        );
8513        let branch = TrieNode::Branch(BranchNode::new(
8514            vec![
8515                RlpNode::word_rlp(&B256::repeat_byte(1)), // blinded child at 0
8516                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(), // revealed at 1
8517            ],
8518            TrieMask::new(0b11),
8519        ));
8520
8521        let mut trie = ParallelSparseTrie::from_root(
8522            branch.clone(),
8523            Some(BranchNodeMasks {
8524                hash_mask: TrieMask::new(0b01),
8525                tree_mask: TrieMask::default(),
8526            }),
8527            false,
8528        )
8529        .unwrap();
8530
8531        // Reveal only the branch and one child, leaving child 0 as a Hash node
8532        trie.reveal_node(
8533            Nibbles::default(),
8534            branch,
8535            Some(BranchNodeMasks {
8536                hash_mask: TrieMask::default(),
8537                tree_mask: TrieMask::new(0b01),
8538            }),
8539        )
8540        .unwrap();
8541        trie.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap();
8542
8543        // The path 0x0... is blinded (Hash node)
8544        // Create an update targeting the blinded path using a full B256 key
8545        let b256_key = B256::ZERO; // starts with 0x0...
8546
8547        let new_value = encode_account_value(42);
8548        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8549        updates.insert(b256_key, LeafUpdate::Changed(new_value));
8550
8551        let proof_targets = RefCell::new(Vec::new());
8552        let prefix_set_len_before = trie.prefix_set.len();
8553        trie.update_leaves(&mut updates, |path, min_len| {
8554            proof_targets.borrow_mut().push((path, min_len));
8555        })
8556        .unwrap();
8557
8558        // Update should remain in map (blinded node)
8559        assert!(!updates.is_empty(), "Update should remain in map when hitting blinded node");
8560
8561        // prefix_set should be unchanged after failed update
8562        assert_eq!(
8563            trie.prefix_set.len(),
8564            prefix_set_len_before,
8565            "prefix_set should be unchanged after failed update on blinded node"
8566        );
8567
8568        // Callback should be invoked
8569        let targets = proof_targets.borrow();
8570        assert!(!targets.is_empty(), "Callback should be invoked for blinded path");
8571
8572        // min_len should equal the blinded node's path length (1 nibble)
8573        assert_eq!(targets[0].1, 1, "min_len should equal blinded node path length");
8574    }
8575
8576    #[test]
8577    fn test_update_leaves_removal() {
8578        use crate::LeafUpdate;
8579        use alloy_primitives::map::B256Map;
8580        use std::cell::RefCell;
8581
8582        let provider = DefaultTrieNodeProvider;
8583        let mut trie = ParallelSparseTrie::default();
8584
8585        // Create two leaves so removal doesn't result in empty trie issues
8586        // Use full-length keys
8587        let b256_key1 = B256::with_last_byte(1);
8588        let b256_key2 = B256::with_last_byte(2);
8589        let key1 = Nibbles::unpack(b256_key1);
8590        let key2 = Nibbles::unpack(b256_key2);
8591        let value = encode_account_value(1);
8592        trie.update_leaf(key1, value.clone(), &provider).unwrap();
8593        trie.update_leaf(key2, value, &provider).unwrap();
8594
8595        // Create an update to remove key1 (empty value = removal)
8596        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8597        updates.insert(b256_key1, LeafUpdate::Changed(vec![])); // empty = removal
8598
8599        let proof_targets = RefCell::new(Vec::new());
8600        trie.update_leaves(&mut updates, |path, min_len| {
8601            proof_targets.borrow_mut().push((path, min_len));
8602        })
8603        .unwrap();
8604
8605        // Removal should succeed: map empty
8606        assert!(updates.is_empty(), "Update map should be empty after successful removal");
8607    }
8608
8609    #[test]
8610    fn test_update_leaves_removal_blinded() {
8611        use crate::LeafUpdate;
8612        use alloy_primitives::map::B256Map;
8613        use std::cell::RefCell;
8614
8615        // Create a trie with a blinded node
8616        // Use a small value that fits in RLP encoding
8617        let small_value = alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec();
8618        let leaf = LeafNode::new(
8619            Nibbles::default(), // short key for RLP encoding
8620            small_value,
8621        );
8622        let branch = TrieNode::Branch(BranchNode::new(
8623            vec![
8624                RlpNode::word_rlp(&B256::repeat_byte(1)), // blinded child at 0
8625                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(), // revealed at 1
8626            ],
8627            TrieMask::new(0b11),
8628        ));
8629
8630        let mut trie = ParallelSparseTrie::from_root(
8631            branch.clone(),
8632            Some(BranchNodeMasks {
8633                hash_mask: TrieMask::new(0b01),
8634                tree_mask: TrieMask::default(),
8635            }),
8636            false,
8637        )
8638        .unwrap();
8639
8640        trie.reveal_node(
8641            Nibbles::default(),
8642            branch,
8643            Some(BranchNodeMasks {
8644                hash_mask: TrieMask::default(),
8645                tree_mask: TrieMask::new(0b01),
8646            }),
8647        )
8648        .unwrap();
8649        trie.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap();
8650
8651        // Simulate having a known value behind the blinded node
8652        let b256_key = B256::ZERO; // starts with 0x0...
8653        let full_path = Nibbles::unpack(b256_key);
8654
8655        // Insert the value into the trie's values map (simulating we know about it)
8656        let old_value = encode_account_value(99);
8657        trie.upper_subtrie.inner.values.insert(full_path, old_value.clone());
8658
8659        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8660        updates.insert(b256_key, LeafUpdate::Changed(vec![])); // empty = removal
8661
8662        let proof_targets = RefCell::new(Vec::new());
8663        let prefix_set_len_before = trie.prefix_set.len();
8664        trie.update_leaves(&mut updates, |path, min_len| {
8665            proof_targets.borrow_mut().push((path, min_len));
8666        })
8667        .unwrap();
8668
8669        // Callback should be invoked
8670        assert!(
8671            !proof_targets.borrow().is_empty(),
8672            "Callback should be invoked when removal hits blinded node"
8673        );
8674
8675        // Update should remain in map
8676        assert!(!updates.is_empty(), "Update should remain in map when removal hits blinded node");
8677
8678        // Original value should be preserved (reverted)
8679        assert_eq!(
8680            trie.upper_subtrie.inner.values.get(&full_path),
8681            Some(&old_value),
8682            "Original value should be preserved after failed removal"
8683        );
8684
8685        // prefix_set should be unchanged after failed removal
8686        assert_eq!(
8687            trie.prefix_set.len(),
8688            prefix_set_len_before,
8689            "prefix_set should be unchanged after failed removal on blinded node"
8690        );
8691    }
8692
8693    #[test]
8694    fn test_update_leaves_removal_branch_collapse_blinded() {
8695        use crate::LeafUpdate;
8696        use alloy_primitives::map::B256Map;
8697        use std::cell::RefCell;
8698
8699        // Create a branch node at root with two children:
8700        // - Child at nibble 0: a blinded Hash node
8701        // - Child at nibble 1: a revealed Leaf node
8702        let small_value = alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec();
8703        let leaf = LeafNode::new(Nibbles::default(), small_value);
8704        let branch = TrieNode::Branch(BranchNode::new(
8705            vec![
8706                RlpNode::word_rlp(&B256::repeat_byte(1)), // blinded child at nibble 0
8707                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(), /* leaf at nibble 1 */
8708            ],
8709            TrieMask::new(0b11),
8710        ));
8711
8712        let mut trie = ParallelSparseTrie::from_root(
8713            branch.clone(),
8714            Some(BranchNodeMasks {
8715                hash_mask: TrieMask::new(0b01), // nibble 0 is hashed
8716                tree_mask: TrieMask::default(),
8717            }),
8718            false,
8719        )
8720        .unwrap();
8721
8722        // Reveal the branch and the leaf at nibble 1, leaving nibble 0 as Hash node
8723        trie.reveal_node(
8724            Nibbles::default(),
8725            branch,
8726            Some(BranchNodeMasks {
8727                hash_mask: TrieMask::default(),
8728                tree_mask: TrieMask::new(0b01),
8729            }),
8730        )
8731        .unwrap();
8732        trie.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap();
8733
8734        // Insert the leaf's value into the values map for the revealed leaf
8735        // Use B256 key that starts with nibble 1 (0x10 has first nibble = 1)
8736        let b256_key = B256::with_last_byte(0x10);
8737        let full_path = Nibbles::unpack(b256_key);
8738        let leaf_value = encode_account_value(42);
8739        trie.upper_subtrie.inner.values.insert(full_path, leaf_value.clone());
8740
8741        // Record state before update_leaves
8742        let prefix_set_len_before = trie.prefix_set.len();
8743        let node_count_before = trie.upper_subtrie.nodes.len() +
8744            trie.lower_subtries
8745                .iter()
8746                .filter_map(|s| s.as_revealed_ref())
8747                .map(|s| s.nodes.len())
8748                .sum::<usize>();
8749
8750        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8751        updates.insert(b256_key, LeafUpdate::Changed(vec![])); // removal
8752
8753        let proof_targets = RefCell::new(Vec::new());
8754        trie.update_leaves(&mut updates, |path, min_len| {
8755            proof_targets.borrow_mut().push((path, min_len));
8756        })
8757        .unwrap();
8758
8759        // Assert: update remains in map (removal blocked by blinded sibling)
8760        assert!(
8761            !updates.is_empty(),
8762            "Update should remain in map when removal would collapse branch with blinded sibling"
8763        );
8764
8765        // Assert: callback was invoked for the blinded path
8766        assert!(
8767            !proof_targets.borrow().is_empty(),
8768            "Callback should be invoked for blinded sibling path"
8769        );
8770
8771        // Assert: prefix_set unchanged (atomic failure)
8772        assert_eq!(
8773            trie.prefix_set.len(),
8774            prefix_set_len_before,
8775            "prefix_set should be unchanged after atomic failure"
8776        );
8777
8778        // Assert: node count unchanged
8779        let node_count_after = trie.upper_subtrie.nodes.len() +
8780            trie.lower_subtries
8781                .iter()
8782                .filter_map(|s| s.as_revealed_ref())
8783                .map(|s| s.nodes.len())
8784                .sum::<usize>();
8785        assert_eq!(
8786            node_count_before, node_count_after,
8787            "Node count should be unchanged after atomic failure"
8788        );
8789
8790        // Assert: the leaf value still exists (not removed)
8791        assert_eq!(
8792            trie.upper_subtrie.inner.values.get(&full_path),
8793            Some(&leaf_value),
8794            "Leaf value should still exist after failed removal"
8795        );
8796    }
8797
8798    #[test]
8799    fn test_update_leaves_touched() {
8800        use crate::LeafUpdate;
8801        use alloy_primitives::map::B256Map;
8802        use std::cell::RefCell;
8803
8804        let provider = DefaultTrieNodeProvider;
8805        let mut trie = ParallelSparseTrie::default();
8806
8807        // Create a leaf in the trie using a full-length key
8808        let b256_key = B256::with_last_byte(42);
8809        let key = Nibbles::unpack(b256_key);
8810        let value = encode_account_value(1);
8811        trie.update_leaf(key, value, &provider).unwrap();
8812
8813        // Create a Touched update for the existing key
8814        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8815        updates.insert(b256_key, LeafUpdate::Touched);
8816
8817        let proof_targets = RefCell::new(Vec::new());
8818        let prefix_set_len_before = trie.prefix_set.len();
8819
8820        trie.update_leaves(&mut updates, |path, min_len| {
8821            proof_targets.borrow_mut().push((path, min_len));
8822        })
8823        .unwrap();
8824
8825        // Update should be removed (path is accessible)
8826        assert!(updates.is_empty(), "Touched update should be removed for accessible path");
8827
8828        // No callback
8829        assert!(
8830            proof_targets.borrow().is_empty(),
8831            "Callback should not be invoked for accessible path"
8832        );
8833
8834        // prefix_set should be unchanged since Touched is read-only
8835        assert_eq!(
8836            trie.prefix_set.len(),
8837            prefix_set_len_before,
8838            "prefix_set should be unchanged for Touched update (read-only)"
8839        );
8840    }
8841
8842    #[test]
8843    fn test_update_leaves_touched_nonexistent() {
8844        use crate::LeafUpdate;
8845        use alloy_primitives::map::B256Map;
8846        use std::cell::RefCell;
8847
8848        let mut trie = ParallelSparseTrie::default();
8849
8850        // Create a Touched update for a key that doesn't exist
8851        let b256_key = B256::with_last_byte(99);
8852        let full_path = Nibbles::unpack(b256_key);
8853
8854        let prefix_set_len_before = trie.prefix_set.len();
8855
8856        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8857        updates.insert(b256_key, LeafUpdate::Touched);
8858
8859        let proof_targets = RefCell::new(Vec::new());
8860        trie.update_leaves(&mut updates, |path, min_len| {
8861            proof_targets.borrow_mut().push((path, min_len));
8862        })
8863        .unwrap();
8864
8865        // Update should be removed (path IS accessible - it's just empty)
8866        assert!(updates.is_empty(), "Touched update should be removed for accessible (empty) path");
8867
8868        // No callback should be invoked (path is revealed, just empty)
8869        assert!(
8870            proof_targets.borrow().is_empty(),
8871            "Callback should not be invoked for accessible path"
8872        );
8873
8874        // prefix_set should NOT be modified (Touched is read-only)
8875        assert_eq!(
8876            trie.prefix_set.len(),
8877            prefix_set_len_before,
8878            "prefix_set should not be modified by Touched update"
8879        );
8880
8881        // No value should be inserted
8882        assert!(
8883            trie.get_leaf_value(&full_path).is_none(),
8884            "No value should exist for non-existent key after Touched update"
8885        );
8886    }
8887
8888    #[test]
8889    fn test_update_leaves_touched_blinded() {
8890        use crate::LeafUpdate;
8891        use alloy_primitives::map::B256Map;
8892        use std::cell::RefCell;
8893
8894        // Create a trie with a blinded node
8895        // Use a small value that fits in RLP encoding
8896        let small_value = alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec();
8897        let leaf = LeafNode::new(
8898            Nibbles::default(), // short key for RLP encoding
8899            small_value,
8900        );
8901        let branch = TrieNode::Branch(BranchNode::new(
8902            vec![
8903                RlpNode::word_rlp(&B256::repeat_byte(1)), // blinded child at 0
8904                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(), // revealed at 1
8905            ],
8906            TrieMask::new(0b11),
8907        ));
8908
8909        let mut trie = ParallelSparseTrie::from_root(
8910            branch.clone(),
8911            Some(BranchNodeMasks {
8912                hash_mask: TrieMask::new(0b01),
8913                tree_mask: TrieMask::default(),
8914            }),
8915            false,
8916        )
8917        .unwrap();
8918
8919        trie.reveal_node(
8920            Nibbles::default(),
8921            branch,
8922            Some(BranchNodeMasks {
8923                hash_mask: TrieMask::default(),
8924                tree_mask: TrieMask::new(0b01),
8925            }),
8926        )
8927        .unwrap();
8928        trie.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap();
8929
8930        // Create a Touched update targeting the blinded path using full B256 key
8931        let b256_key = B256::ZERO; // starts with 0x0...
8932
8933        let mut updates: B256Map<LeafUpdate> = B256Map::default();
8934        updates.insert(b256_key, LeafUpdate::Touched);
8935
8936        let proof_targets = RefCell::new(Vec::new());
8937        let prefix_set_len_before = trie.prefix_set.len();
8938        trie.update_leaves(&mut updates, |path, min_len| {
8939            proof_targets.borrow_mut().push((path, min_len));
8940        })
8941        .unwrap();
8942
8943        // Callback should be invoked
8944        assert!(!proof_targets.borrow().is_empty(), "Callback should be invoked for blinded path");
8945
8946        // Update should remain in map
8947        assert!(!updates.is_empty(), "Touched update should remain in map for blinded path");
8948
8949        // prefix_set should be unchanged since Touched is read-only
8950        assert_eq!(
8951            trie.prefix_set.len(),
8952            prefix_set_len_before,
8953            "prefix_set should be unchanged for Touched update on blinded path"
8954        );
8955    }
8956
8957    #[test]
8958    fn test_update_leaves_deduplication() {
8959        use crate::LeafUpdate;
8960        use alloy_primitives::map::B256Map;
8961        use std::cell::RefCell;
8962
8963        // Create a trie with a blinded node
8964        // Use a small value that fits in RLP encoding
8965        let small_value = alloy_rlp::encode_fixed_size(&U256::from(1)).to_vec();
8966        let leaf = LeafNode::new(
8967            Nibbles::default(), // short key for RLP encoding
8968            small_value,
8969        );
8970        let branch = TrieNode::Branch(BranchNode::new(
8971            vec![
8972                RlpNode::word_rlp(&B256::repeat_byte(1)), // blinded child at 0
8973                RlpNode::from_raw_rlp(&alloy_rlp::encode(leaf.clone())).unwrap(), // revealed at 1
8974            ],
8975            TrieMask::new(0b11),
8976        ));
8977
8978        let mut trie = ParallelSparseTrie::from_root(
8979            branch.clone(),
8980            Some(BranchNodeMasks {
8981                hash_mask: TrieMask::new(0b01),
8982                tree_mask: TrieMask::default(),
8983            }),
8984            false,
8985        )
8986        .unwrap();
8987
8988        trie.reveal_node(
8989            Nibbles::default(),
8990            branch,
8991            Some(BranchNodeMasks {
8992                hash_mask: TrieMask::default(),
8993                tree_mask: TrieMask::new(0b01),
8994            }),
8995        )
8996        .unwrap();
8997        trie.reveal_node(Nibbles::from_nibbles([0x1]), TrieNode::Leaf(leaf), None).unwrap();
8998
8999        // Create multiple updates that would all hit the same blinded node at path 0x0
9000        // Use full B256 keys that all start with 0x0
9001        let b256_key1 = B256::ZERO;
9002        let b256_key2 = B256::with_last_byte(1); // still starts with 0x0
9003        let b256_key3 = B256::with_last_byte(2); // still starts with 0x0
9004
9005        let mut updates: B256Map<LeafUpdate> = B256Map::default();
9006        let value = encode_account_value(42);
9007
9008        updates.insert(b256_key1, LeafUpdate::Changed(value.clone()));
9009        updates.insert(b256_key2, LeafUpdate::Changed(value.clone()));
9010        updates.insert(b256_key3, LeafUpdate::Changed(value));
9011
9012        let proof_targets = RefCell::new(Vec::new());
9013        trie.update_leaves(&mut updates, |path, min_len| {
9014            proof_targets.borrow_mut().push((path, min_len));
9015        })
9016        .unwrap();
9017
9018        // The callback should be invoked 3 times - once for each unique full_path
9019        // The deduplication is by (full_path, min_len), not by blinded node
9020        let targets = proof_targets.borrow();
9021        assert_eq!(targets.len(), 3, "Callback should be invoked for each unique key");
9022
9023        // All should have the same min_len (1) since they all hit blinded node at path 0x0
9024        for (_, min_len) in targets.iter() {
9025            assert_eq!(*min_len, 1, "All should have min_len 1 from blinded node at 0x0");
9026        }
9027    }
9028
9029    #[test]
9030    fn test_update_leaves_node_not_found_in_provider_atomicity() {
9031        use crate::LeafUpdate;
9032        use alloy_primitives::map::B256Map;
9033        use std::cell::RefCell;
9034
9035        // Create a trie with retain_updates enabled (this triggers the code path that
9036        // can return NodeNotFoundInProvider when an extension node's child needs revealing).
9037        //
9038        // Structure: Extension at root -> Hash node (blinded child)
9039        // When we try to insert a new leaf that would split the extension, with retain_updates
9040        // enabled, it tries to reveal the hash child via the provider. With NoRevealProvider,
9041        // this returns NodeNotFoundInProvider.
9042
9043        let child_hash = B256::repeat_byte(0xAB);
9044        let extension = TrieNode::Extension(ExtensionNode::new(
9045            Nibbles::from_nibbles([0x1, 0x2, 0x3]),
9046            RlpNode::word_rlp(&child_hash),
9047        ));
9048
9049        // Create trie with retain_updates = true
9050        let mut trie =
9051            ParallelSparseTrie::from_root(extension, None, true).expect("from_root failed");
9052
9053        // Record state before update_leaves
9054        let prefix_set_len_before = trie.prefix_set.len();
9055        let node_count_before = trie.upper_subtrie.nodes.len() +
9056            trie.lower_subtries
9057                .iter()
9058                .filter_map(|s| s.as_revealed_ref())
9059                .map(|s| s.nodes.len())
9060                .sum::<usize>();
9061        let value_count_before = trie.upper_subtrie.inner.values.len() +
9062            trie.lower_subtries
9063                .iter()
9064                .filter_map(|s| s.as_revealed_ref())
9065                .map(|s| s.inner.values.len())
9066                .sum::<usize>();
9067
9068        // Create an update that would cause an extension split.
9069        // The key starts with 0x1 but diverges from 0x123... at the second nibble.
9070        let b256_key = {
9071            let mut k = B256::ZERO;
9072            k.0[0] = 0x14; // nibbles: 1, 4 - matches first nibble, diverges at second
9073            k
9074        };
9075
9076        let new_value = encode_account_value(42);
9077        let mut updates: B256Map<LeafUpdate> = B256Map::default();
9078        updates.insert(b256_key, LeafUpdate::Changed(new_value));
9079
9080        let proof_targets = RefCell::new(Vec::new());
9081        trie.update_leaves(&mut updates, |path, min_len| {
9082            proof_targets.borrow_mut().push((path, min_len));
9083        })
9084        .expect("update_leaves should succeed");
9085
9086        // Assert: update remains in map (NodeNotFoundInProvider is retriable)
9087        assert!(
9088            !updates.is_empty(),
9089            "Update should remain in map when NodeNotFoundInProvider occurs"
9090        );
9091        assert!(
9092            updates.contains_key(&b256_key),
9093            "The specific key should be re-inserted for retry"
9094        );
9095
9096        // Assert: callback was invoked
9097        let targets = proof_targets.borrow();
9098        assert!(!targets.is_empty(), "Callback should be invoked for NodeNotFoundInProvider");
9099
9100        // Assert: prefix_set unchanged (atomic - no partial state)
9101        assert_eq!(
9102            trie.prefix_set.len(),
9103            prefix_set_len_before,
9104            "prefix_set should be unchanged after atomic failure"
9105        );
9106
9107        // Assert: node count unchanged (no structural changes persisted)
9108        let node_count_after = trie.upper_subtrie.nodes.len() +
9109            trie.lower_subtries
9110                .iter()
9111                .filter_map(|s| s.as_revealed_ref())
9112                .map(|s| s.nodes.len())
9113                .sum::<usize>();
9114        assert_eq!(
9115            node_count_before, node_count_after,
9116            "Node count should be unchanged after atomic failure"
9117        );
9118
9119        // Assert: value count unchanged (no values left dangling)
9120        let value_count_after = trie.upper_subtrie.inner.values.len() +
9121            trie.lower_subtries
9122                .iter()
9123                .filter_map(|s| s.as_revealed_ref())
9124                .map(|s| s.inner.values.len())
9125                .sum::<usize>();
9126        assert_eq!(
9127            value_count_before, value_count_after,
9128            "Value count should be unchanged after atomic failure (no dangling values)"
9129        );
9130    }
9131
9132    #[test]
9133    fn test_nibbles_to_padded_b256() {
9134        // Empty nibbles should produce all zeros
9135        let empty = Nibbles::default();
9136        assert_eq!(ParallelSparseTrie::nibbles_to_padded_b256(&empty), B256::ZERO);
9137
9138        // Full 64-nibble path should round-trip through B256
9139        let full_key = b256!("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
9140        let full_nibbles = Nibbles::unpack(full_key);
9141        assert_eq!(ParallelSparseTrie::nibbles_to_padded_b256(&full_nibbles), full_key);
9142
9143        // Partial nibbles should be left-aligned with zero padding on the right
9144        // 4 nibbles [0x1, 0x2, 0x3, 0x4] should pack to 0x1234...00
9145        let partial = Nibbles::from_nibbles_unchecked([0x1, 0x2, 0x3, 0x4]);
9146        let expected = b256!("1234000000000000000000000000000000000000000000000000000000000000");
9147        assert_eq!(ParallelSparseTrie::nibbles_to_padded_b256(&partial), expected);
9148
9149        // Single nibble
9150        let single = Nibbles::from_nibbles_unchecked([0xf]);
9151        let expected_single =
9152            b256!("f000000000000000000000000000000000000000000000000000000000000000");
9153        assert_eq!(ParallelSparseTrie::nibbles_to_padded_b256(&single), expected_single);
9154    }
9155
9156    #[test]
9157    fn test_memory_size() {
9158        // Test that memory_size returns a reasonable value for an empty trie
9159        let trie = ParallelSparseTrie::default();
9160        let empty_size = trie.memory_size();
9161
9162        // Should at least be the size of the struct itself
9163        assert!(empty_size >= core::mem::size_of::<ParallelSparseTrie>());
9164
9165        // Create a trie with some data. Set up a root branch with children at 0x1 and
9166        // 0x5, and branches at [0x1] and [0x5] pointing to 0x2 and 0x6 respectively,
9167        // so the lower subtries at [0x1, 0x2] and [0x5, 0x6] are reachable.
9168        let root_branch = create_branch_node_with_children(
9169            &[0x1, 0x5],
9170            [
9171                RlpNode::word_rlp(&B256::repeat_byte(0xAA)),
9172                RlpNode::word_rlp(&B256::repeat_byte(0xBB)),
9173            ],
9174        );
9175        let mut trie = ParallelSparseTrie::from_root(root_branch, None, false).unwrap();
9176
9177        let branch_at_1 =
9178            create_branch_node_with_children(&[0x2], [RlpNode::word_rlp(&B256::repeat_byte(0xCC))]);
9179        let branch_at_5 =
9180            create_branch_node_with_children(&[0x6], [RlpNode::word_rlp(&B256::repeat_byte(0xDD))]);
9181        trie.reveal_nodes(&mut [
9182            ProofTrieNode {
9183                path: Nibbles::from_nibbles_unchecked([0x1]),
9184                node: branch_at_1,
9185                masks: None,
9186            },
9187            ProofTrieNode {
9188                path: Nibbles::from_nibbles_unchecked([0x5]),
9189                node: branch_at_5,
9190                masks: None,
9191            },
9192        ])
9193        .unwrap();
9194
9195        let mut nodes = vec![
9196            ProofTrieNode {
9197                path: Nibbles::from_nibbles_unchecked([0x1, 0x2]),
9198                node: TrieNode::Leaf(LeafNode {
9199                    key: Nibbles::from_nibbles_unchecked([0x3, 0x4]),
9200                    value: vec![1, 2, 3],
9201                }),
9202                masks: None,
9203            },
9204            ProofTrieNode {
9205                path: Nibbles::from_nibbles_unchecked([0x5, 0x6]),
9206                node: TrieNode::Leaf(LeafNode {
9207                    key: Nibbles::from_nibbles_unchecked([0x7, 0x8]),
9208                    value: vec![4, 5, 6],
9209                }),
9210                masks: None,
9211            },
9212        ];
9213        trie.reveal_nodes(&mut nodes).unwrap();
9214
9215        let populated_size = trie.memory_size();
9216
9217        // Populated trie should use more memory than an empty one
9218        assert!(populated_size > empty_size);
9219    }
9220}