Skip to main content

reth_trie_sparse/
trie.rs

1use crate::{
2    provider::TrieNodeProvider, LeafUpdate, ParallelSparseTrie, SparseTrie as SparseTrieTrait,
3    SparseTrieUpdates,
4};
5use alloc::{boxed::Box, vec::Vec};
6use alloy_primitives::{map::B256Map, B256};
7use reth_execution_errors::{SparseTrieErrorKind, SparseTrieResult};
8use reth_trie_common::{BranchNodeMasks, Nibbles, RlpNode, TrieMask, TrieNode};
9use tracing::instrument;
10
11/// A sparse trie that is either in a "blind" state (no nodes are revealed, root node hash is
12/// unknown) or in a "revealed" state (root node has been revealed and the trie can be updated).
13///
14/// In blind mode the trie does not contain any decoded node data, which saves memory but
15/// prevents direct access to node contents. The revealed mode stores decoded nodes along
16/// with additional information such as values, allowing direct manipulation.
17///
18/// The sparse trie design is optimised for:
19/// 1. Memory efficiency - only revealed nodes are loaded into memory
20/// 2. Update tracking - changes to the trie structure can be tracked and selectively persisted
21/// 3. Incremental operations - nodes can be revealed as needed without loading the entire trie.
22///    This is what gives rise to the notion of a "sparse" trie.
23#[derive(PartialEq, Eq, Debug, Clone)]
24pub enum RevealableSparseTrie<T = ParallelSparseTrie> {
25    /// The trie is blind -- no nodes have been revealed
26    ///
27    /// This is the default state. In this state, the trie cannot be directly queried or modified
28    /// until nodes are revealed.
29    ///
30    /// In this state the `RevealableSparseTrie` can optionally carry with it a cleared
31    /// sparse trie. This allows for reusing the trie's allocations between payload executions.
32    Blind(Option<Box<T>>),
33    /// Some nodes in the Trie have been revealed.
34    ///
35    /// In this state, the trie can be queried and modified for the parts
36    /// that have been revealed. Other parts remain blind and require revealing
37    /// before they can be accessed.
38    Revealed(Box<T>),
39}
40
41impl<T: Default> Default for RevealableSparseTrie<T> {
42    fn default() -> Self {
43        Self::Blind(None)
44    }
45}
46
47impl<T: SparseTrieTrait + Default> RevealableSparseTrie<T> {
48    /// Creates a new revealed but empty sparse trie with `SparseNode::Empty` as root node.
49    pub fn revealed_empty() -> Self {
50        Self::Revealed(Box::default())
51    }
52
53    /// Reveals the root node, converting a blind trie into a revealed one.
54    ///
55    /// If the trie is blinded, its root node is replaced with `root`.
56    ///
57    /// The `masks` are used to determine how the node's children are stored.
58    /// The `retain_updates` flag controls whether changes to the trie structure
59    /// should be tracked.
60    ///
61    /// # Returns
62    ///
63    /// A mutable reference to the underlying [`RevealableSparseTrie`](SparseTrieTrait).
64    pub fn reveal_root(
65        &mut self,
66        root: TrieNode,
67        masks: Option<BranchNodeMasks>,
68        retain_updates: bool,
69    ) -> SparseTrieResult<&mut T> {
70        // if `Blind`, we initialize the revealed trie with the given root node, using a
71        // pre-allocated trie if available.
72        if self.is_blind() {
73            let mut revealed_trie = if let Self::Blind(Some(cleared_trie)) = core::mem::take(self) {
74                cleared_trie
75            } else {
76                Box::default()
77            };
78
79            revealed_trie.set_root(root, masks, retain_updates)?;
80            *self = Self::Revealed(revealed_trie);
81        }
82
83        Ok(self.as_revealed_mut().unwrap())
84    }
85}
86
87impl<T: SparseTrieTrait> RevealableSparseTrie<T> {
88    /// Creates a new blind sparse trie.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use reth_trie_sparse::{provider::DefaultTrieNodeProvider, RevealableSparseTrie};
94    ///
95    /// let trie = <RevealableSparseTrie>::blind();
96    /// assert!(trie.is_blind());
97    /// let trie = <RevealableSparseTrie>::default();
98    /// assert!(trie.is_blind());
99    /// ```
100    pub const fn blind() -> Self {
101        Self::Blind(None)
102    }
103
104    /// Creates a new blind sparse trie, clearing and later reusing the given
105    /// [`RevealableSparseTrie`](SparseTrieTrait).
106    pub fn blind_from(mut trie: T) -> Self {
107        trie.clear();
108        Self::Blind(Some(Box::new(trie)))
109    }
110
111    /// Returns `true` if the sparse trie has no revealed nodes.
112    pub const fn is_blind(&self) -> bool {
113        matches!(self, Self::Blind(_))
114    }
115
116    /// Returns `true` if the sparse trie is revealed.
117    pub const fn is_revealed(&self) -> bool {
118        matches!(self, Self::Revealed(_))
119    }
120
121    /// Returns an immutable reference to the underlying revealed sparse trie.
122    ///
123    /// Returns `None` if the trie is blinded.
124    pub const fn as_revealed_ref(&self) -> Option<&T> {
125        if let Self::Revealed(revealed) = self {
126            Some(revealed)
127        } else {
128            None
129        }
130    }
131
132    /// Returns a mutable reference to the underlying revealed sparse trie.
133    ///
134    /// Returns `None` if the trie is blinded.
135    pub fn as_revealed_mut(&mut self) -> Option<&mut T> {
136        if let Self::Revealed(revealed) = self {
137            Some(revealed)
138        } else {
139            None
140        }
141    }
142
143    /// Wipes the trie by removing all nodes and values,
144    /// and resetting the trie to only contain an empty root node.
145    ///
146    /// Note: This method will error if the trie is blinded.
147    pub fn wipe(&mut self) -> SparseTrieResult<()> {
148        let revealed = self.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?;
149        revealed.wipe();
150        Ok(())
151    }
152
153    /// Calculates the root hash of the trie.
154    ///
155    /// This will update any remaining dirty nodes before computing the root hash.
156    /// "dirty" nodes are nodes that need their hashes to be recomputed because one or more of their
157    /// children's hashes have changed.
158    ///
159    /// # Returns
160    ///
161    /// - `Some(B256)` with the calculated root hash if the trie is revealed.
162    /// - `None` if the trie is still blind.
163    pub fn root(&mut self) -> Option<B256> {
164        Some(self.as_revealed_mut()?.root())
165    }
166
167    /// Returns true if the root node is cached and does not need any recomputation.
168    pub fn is_root_cached(&self) -> bool {
169        self.as_revealed_ref().is_some_and(|trie| trie.is_root_cached())
170    }
171
172    /// Returns the root hash along with any accumulated update information.
173    ///
174    /// This is useful for when you need both the root hash and information about
175    /// what nodes were modified, which can be used to efficiently update
176    /// an external database.
177    ///
178    /// # Returns
179    ///
180    /// An `Option` tuple consisting of:
181    ///  - The trie root hash (`B256`).
182    ///  - A [`SparseTrieUpdates`] structure containing information about updated nodes.
183    ///  - `None` if the trie is still blind.
184    pub fn root_with_updates(&mut self) -> Option<(B256, SparseTrieUpdates)> {
185        let revealed = self.as_revealed_mut()?;
186        Some((revealed.root(), revealed.take_updates()))
187    }
188
189    /// Clears this trie, setting it to a blind state.
190    ///
191    /// If this instance was revealed, or was itself a `Blind` with a pre-allocated
192    /// [`RevealableSparseTrie`](SparseTrieTrait), this will set to `Blind` carrying a cleared
193    /// pre-allocated [`RevealableSparseTrie`](SparseTrieTrait).
194    #[inline]
195    pub fn clear(&mut self) {
196        *self = match core::mem::replace(self, Self::blind()) {
197            s @ Self::Blind(_) => s,
198            Self::Revealed(mut trie) => {
199                trie.clear();
200                Self::Blind(Some(trie))
201            }
202        };
203    }
204
205    /// Updates (or inserts) a leaf at the given key path with the specified RLP-encoded value.
206    ///
207    /// # Errors
208    ///
209    /// Returns an error if the trie is still blind, or if the update fails.
210    #[instrument(level = "trace", target = "trie::sparse", skip_all)]
211    pub fn update_leaf(
212        &mut self,
213        path: Nibbles,
214        value: Vec<u8>,
215        provider: impl TrieNodeProvider,
216    ) -> SparseTrieResult<()> {
217        let revealed = self.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?;
218        revealed.update_leaf(path, value, provider)?;
219        Ok(())
220    }
221
222    /// Removes a leaf node at the specified key path.
223    ///
224    /// # Errors
225    ///
226    /// Returns an error if the trie is still blind, or if the leaf cannot be removed
227    #[instrument(level = "trace", target = "trie::sparse", skip_all)]
228    pub fn remove_leaf(
229        &mut self,
230        path: &Nibbles,
231        provider: impl TrieNodeProvider,
232    ) -> SparseTrieResult<()> {
233        let revealed = self.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?;
234        revealed.remove_leaf(path, provider)?;
235        Ok(())
236    }
237
238    /// Shrinks the capacity of the sparse trie's node storage.
239    /// Works for both revealed and blind tries with allocated storage.
240    pub fn shrink_nodes_to(&mut self, size: usize) {
241        match self {
242            Self::Blind(Some(trie)) | Self::Revealed(trie) => {
243                trie.shrink_nodes_to(size);
244            }
245            _ => {}
246        }
247    }
248
249    /// Shrinks the capacity of the sparse trie's value storage.
250    /// Works for both revealed and blind tries with allocated storage.
251    pub fn shrink_values_to(&mut self, size: usize) {
252        match self {
253            Self::Blind(Some(trie)) | Self::Revealed(trie) => {
254                trie.shrink_values_to(size);
255            }
256            _ => {}
257        }
258    }
259}
260
261impl<T: SparseTrieTrait + Default> RevealableSparseTrie<T> {
262    /// Applies batch leaf updates to the sparse trie.
263    ///
264    /// For blind tries, all updates are kept in the map and proof targets are emitted
265    /// for every key (with `min_len = 0` since nothing is revealed).
266    ///
267    /// For revealed tries, delegates to the inner implementation which will:
268    /// - Apply updates where possible
269    /// - Keep blocked updates in the map
270    /// - Emit proof targets for blinded paths
271    pub fn update_leaves(
272        &mut self,
273        updates: &mut B256Map<LeafUpdate>,
274        mut proof_required_fn: impl FnMut(B256, u8),
275    ) -> SparseTrieResult<()> {
276        match self {
277            Self::Blind(_) => {
278                // Nothing is revealed - emit proof targets for all keys with min_len = 0
279                for key in updates.keys() {
280                    proof_required_fn(*key, 0);
281                }
282                // All updates remain in the map for retry after proofs are fetched
283                Ok(())
284            }
285            Self::Revealed(trie) => trie.update_leaves(updates, proof_required_fn),
286        }
287    }
288}
289
290/// Enum representing sparse trie node type.
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292pub enum SparseNodeType {
293    /// Empty trie node.
294    Empty,
295    /// A placeholder that stores only the hash for a node that has not been fully revealed.
296    Hash,
297    /// Sparse leaf node.
298    Leaf,
299    /// Sparse extension node.
300    Extension {
301        /// A flag indicating whether the extension node should be stored in the database.
302        store_in_db_trie: Option<bool>,
303    },
304    /// Sparse branch node.
305    Branch {
306        /// A flag indicating whether the branch node should be stored in the database.
307        store_in_db_trie: Option<bool>,
308    },
309}
310
311impl SparseNodeType {
312    /// Returns true if the node is a hash node.
313    pub const fn is_hash(&self) -> bool {
314        matches!(self, Self::Hash)
315    }
316
317    /// Returns true if the node is a branch node.
318    pub const fn is_branch(&self) -> bool {
319        matches!(self, Self::Branch { .. })
320    }
321
322    /// Returns true if the node should be stored in the database.
323    pub const fn store_in_db_trie(&self) -> Option<bool> {
324        match *self {
325            Self::Extension { store_in_db_trie } | Self::Branch { store_in_db_trie } => {
326                store_in_db_trie
327            }
328            _ => None,
329        }
330    }
331}
332
333/// Enum representing trie nodes in sparse trie.
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub enum SparseNode {
336    /// Empty trie node.
337    Empty,
338    /// The hash of the node that was not revealed.
339    Hash(B256),
340    /// Sparse leaf node with remaining key suffix.
341    Leaf {
342        /// Remaining key suffix for the leaf node.
343        key: Nibbles,
344        /// Pre-computed hash of the sparse node.
345        /// Can be reused unless this trie path has been updated.
346        hash: Option<B256>,
347    },
348    /// Sparse extension node with key.
349    Extension {
350        /// The key slice stored by this extension node.
351        key: Nibbles,
352        /// Pre-computed hash of the sparse node.
353        /// Can be reused unless this trie path has been updated.
354        ///
355        /// If [`None`], then the value is not known and should be calculated from scratch.
356        hash: Option<B256>,
357        /// Pre-computed flag indicating whether the trie node should be stored in the database.
358        /// Can be reused unless this trie path has been updated.
359        ///
360        /// If [`None`], then the value is not known and should be calculated from scratch.
361        store_in_db_trie: Option<bool>,
362    },
363    /// Sparse branch node with state mask.
364    Branch {
365        /// The bitmask representing children present in the branch node.
366        state_mask: TrieMask,
367        /// Pre-computed hash of the sparse node.
368        /// Can be reused unless this trie path has been updated.
369        ///
370        /// If [`None`], then the value is not known and should be calculated from scratch.
371        hash: Option<B256>,
372        /// Pre-computed flag indicating whether the trie node should be stored in the database.
373        /// Can be reused unless this trie path has been updated.
374        ///
375        /// If [`None`], then the value is not known and should be calculated from scratch.
376        store_in_db_trie: Option<bool>,
377    },
378}
379
380impl SparseNode {
381    /// Create new sparse node from [`TrieNode`].
382    pub fn from_node(node: TrieNode) -> Self {
383        match node {
384            TrieNode::EmptyRoot => Self::Empty,
385            TrieNode::Leaf(leaf) => Self::new_leaf(leaf.key),
386            TrieNode::Extension(ext) => Self::new_ext(ext.key),
387            TrieNode::Branch(branch) => Self::new_branch(branch.state_mask),
388        }
389    }
390
391    /// Create new [`SparseNode::Branch`] from state mask.
392    pub const fn new_branch(state_mask: TrieMask) -> Self {
393        Self::Branch { state_mask, hash: None, store_in_db_trie: None }
394    }
395
396    /// Create new [`SparseNode::Branch`] with two bits set.
397    pub const fn new_split_branch(bit_a: u8, bit_b: u8) -> Self {
398        let state_mask = TrieMask::new(
399            // set bits for both children
400            (1u16 << bit_a) | (1u16 << bit_b),
401        );
402        Self::Branch { state_mask, hash: None, store_in_db_trie: None }
403    }
404
405    /// Create new [`SparseNode::Extension`] from the key slice.
406    pub const fn new_ext(key: Nibbles) -> Self {
407        Self::Extension { key, hash: None, store_in_db_trie: None }
408    }
409
410    /// Create new [`SparseNode::Leaf`] from leaf key and value.
411    pub const fn new_leaf(key: Nibbles) -> Self {
412        Self::Leaf { key, hash: None }
413    }
414
415    /// Returns `true` if the node is a hash node.
416    pub const fn is_hash(&self) -> bool {
417        matches!(self, Self::Hash(_))
418    }
419
420    /// Returns the hash of the node if it exists.
421    pub const fn hash(&self) -> Option<B256> {
422        match self {
423            Self::Empty => None,
424            Self::Hash(hash) => Some(*hash),
425            Self::Leaf { hash, .. } | Self::Extension { hash, .. } | Self::Branch { hash, .. } => {
426                *hash
427            }
428        }
429    }
430
431    /// Sets the hash of the node for testing purposes.
432    ///
433    /// For [`SparseNode::Empty`] and [`SparseNode::Hash`] nodes, this method does nothing.
434    #[cfg(any(test, feature = "test-utils"))]
435    pub const fn set_hash(&mut self, new_hash: Option<B256>) {
436        match self {
437            Self::Empty | Self::Hash(_) => {
438                // Cannot set hash for Empty or Hash nodes
439            }
440            Self::Leaf { hash, .. } | Self::Extension { hash, .. } | Self::Branch { hash, .. } => {
441                *hash = new_hash;
442            }
443        }
444    }
445
446    /// Returns the memory size of this node in bytes.
447    pub const fn memory_size(&self) -> usize {
448        match self {
449            Self::Empty | Self::Hash(_) | Self::Branch { .. } => core::mem::size_of::<Self>(),
450            Self::Leaf { key, .. } | Self::Extension { key, .. } => {
451                core::mem::size_of::<Self>() + key.len()
452            }
453        }
454    }
455}
456
457/// RLP node stack item.
458#[derive(Clone, PartialEq, Eq, Debug)]
459pub struct RlpNodeStackItem {
460    /// Path to the node.
461    pub path: Nibbles,
462    /// RLP node.
463    pub rlp_node: RlpNode,
464    /// Type of the node.
465    pub node_type: SparseNodeType,
466}
467
468impl SparseTrieUpdates {
469    /// Create new wiped sparse trie updates.
470    pub fn wiped() -> Self {
471        Self { wiped: true, ..Default::default() }
472    }
473
474    /// Clears the updates, but keeps the backing data structures allocated.
475    ///
476    /// Sets `wiped` to `false`.
477    pub fn clear(&mut self) {
478        self.updated_nodes.clear();
479        self.removed_nodes.clear();
480        self.wiped = false;
481    }
482
483    /// Extends the updates with another set of updates.
484    pub fn extend(&mut self, other: Self) {
485        self.updated_nodes.extend(other.updated_nodes);
486        self.removed_nodes.extend(other.removed_nodes);
487        self.wiped |= other.wiped;
488    }
489}