reth_trie_sparse/
traits.rs

1//! Traits for sparse trie implementations.
2
3use core::fmt::Debug;
4
5use alloc::{borrow::Cow, vec, vec::Vec};
6use alloy_primitives::{
7    map::{HashMap, HashSet},
8    B256,
9};
10use alloy_trie::{BranchNodeCompact, TrieMask};
11use reth_execution_errors::SparseTrieResult;
12use reth_trie_common::{Nibbles, TrieNode};
13
14use crate::provider::TrieNodeProvider;
15
16/// Trait defining common operations for revealed sparse trie implementations.
17///
18/// This trait abstracts over different sparse trie implementations (serial vs parallel)
19/// while providing a unified interface for the core trie operations needed by the
20/// [`crate::SparseTrie`] enum.
21pub trait SparseTrieInterface: Sized + Debug + Send + Sync {
22    /// Configures the trie to have the given root node revealed.
23    ///
24    /// # Arguments
25    ///
26    /// * `root` - The root node to reveal
27    /// * `masks` - Trie masks for root branch node
28    /// * `retain_updates` - Whether to track updates
29    ///
30    /// # Returns
31    ///
32    /// Self if successful, or an error if revealing fails.
33    ///
34    /// # Panics
35    ///
36    /// May panic if the trie is not new/cleared, and has already revealed nodes.
37    fn with_root(
38        self,
39        root: TrieNode,
40        masks: TrieMasks,
41        retain_updates: bool,
42    ) -> SparseTrieResult<Self>;
43
44    /// Configures the trie to retain information about updates.
45    ///
46    /// If `retain_updates` is true, the trie will record branch node updates
47    /// and deletions. This information can be used to efficiently update
48    /// an external database.
49    ///
50    /// # Arguments
51    ///
52    /// * `retain_updates` - Whether to track updates
53    ///
54    /// # Returns
55    ///
56    /// Self for method chaining.
57    fn with_updates(self, retain_updates: bool) -> Self;
58
59    /// Reserves capacity for additional trie nodes.
60    ///
61    /// # Arguments
62    ///
63    /// * `additional` - The number of additional trie nodes to reserve capacity for.
64    fn reserve_nodes(&mut self, _additional: usize) {}
65
66    /// The single-node version of `reveal_nodes`.
67    ///
68    /// # Returns
69    ///
70    /// `Ok(())` if successful, or an error if the node was not revealed.
71    fn reveal_node(
72        &mut self,
73        path: Nibbles,
74        node: TrieNode,
75        masks: TrieMasks,
76    ) -> SparseTrieResult<()> {
77        self.reveal_nodes(vec![RevealedSparseNode { path, node, masks }])
78    }
79
80    /// Reveals one or more trie nodes if they have not been revealed before.
81    ///
82    /// This function decodes trie nodes and inserts them into the trie structure. It handles
83    /// different node types (leaf, extension, branch) by appropriately adding them to the trie and
84    /// recursively revealing their children.
85    ///
86    /// # Arguments
87    ///
88    /// * `nodes` - The nodes to be revealed, each having a path and optional set of branch node
89    ///   masks. The nodes will be unsorted.
90    ///
91    /// # Returns
92    ///
93    /// `Ok(())` if successful, or an error if any of the nodes was not revealed.
94    fn reveal_nodes(&mut self, nodes: Vec<RevealedSparseNode>) -> SparseTrieResult<()>;
95
96    /// Updates the value of a leaf node at the specified path.
97    ///
98    /// If the leaf doesn't exist, it will be created.
99    /// If it does exist, its value will be updated.
100    ///
101    /// # Arguments
102    ///
103    /// * `full_path` - The full path to the leaf
104    /// * `value` - The new value for the leaf
105    /// * `provider` - The trie provider for resolving missing nodes
106    ///
107    /// # Returns
108    ///
109    /// `Ok(())` if successful, or an error if the update failed.
110    fn update_leaf<P: TrieNodeProvider>(
111        &mut self,
112        full_path: Nibbles,
113        value: Vec<u8>,
114        provider: P,
115    ) -> SparseTrieResult<()>;
116
117    /// Removes a leaf node at the specified path.
118    ///
119    /// This will also handle collapsing the trie structure as needed
120    /// (e.g., removing branch nodes that become unnecessary).
121    ///
122    /// # Arguments
123    ///
124    /// * `full_path` - The full path to the leaf to remove
125    /// * `provider` - The trie node provider for resolving missing nodes
126    ///
127    /// # Returns
128    ///
129    /// `Ok(())` if successful, or an error if the removal failed.
130    fn remove_leaf<P: TrieNodeProvider>(
131        &mut self,
132        full_path: &Nibbles,
133        provider: P,
134    ) -> SparseTrieResult<()>;
135
136    /// Calculates and returns the root hash of the trie.
137    ///
138    /// This processes any dirty nodes by updating their RLP encodings
139    /// and returns the root hash.
140    ///
141    /// # Returns
142    ///
143    /// The root hash of the trie.
144    fn root(&mut self) -> B256;
145
146    /// Recalculates and updates the RLP hashes of subtries deeper than a certain level. The level
147    /// is defined in the implementation.
148    ///
149    /// The root node is considered to be at level 0. This method is useful for optimizing
150    /// hash recalculations after localized changes to the trie structure.
151    fn update_subtrie_hashes(&mut self);
152
153    /// Retrieves a reference to the leaf value at the specified path.
154    ///
155    /// # Arguments
156    ///
157    /// * `full_path` - The full path to the leaf value
158    ///
159    /// # Returns
160    ///
161    /// A reference to the leaf value stored at the given full path, if it is revealed.
162    ///
163    /// Note: a value can exist in the full trie and this function still returns `None`
164    /// because the value has not been revealed.
165    ///
166    /// Hence a `None` indicates two possibilities:
167    /// - The value does not exists in the trie, so it cannot be revealed
168    /// - The value has not yet been revealed. In order to determine which is true, one would need
169    ///   an exclusion proof.
170    fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>>;
171
172    /// Attempts to find a leaf node at the specified path.
173    ///
174    /// This method traverses the trie from the root down to the given path, checking
175    /// if a leaf exists at that path. It can be used to verify the existence of a leaf
176    /// or to generate an exclusion proof (proof that a leaf does not exist).
177    ///
178    /// # Parameters
179    ///
180    /// - `full_path`: The path to search for.
181    /// - `expected_value`: Optional expected value. If provided, will verify the leaf value
182    ///   matches.
183    ///
184    /// # Returns
185    ///
186    /// - `Ok(LeafLookup::Exists)` if the leaf exists with the expected value.
187    /// - `Ok(LeafLookup::NonExistent)` if the leaf definitely does not exist (exclusion proof).
188    /// - `Err(LeafLookupError)` if the search encountered a blinded node or found a different
189    ///   value.
190    fn find_leaf(
191        &self,
192        full_path: &Nibbles,
193        expected_value: Option<&Vec<u8>>,
194    ) -> Result<LeafLookup, LeafLookupError>;
195
196    /// Returns a reference to the current sparse trie updates.
197    ///
198    /// If no updates have been made/recorded, returns an empty update set.
199    fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates>;
200
201    /// Consumes and returns the currently accumulated trie updates.
202    ///
203    /// This is useful when you want to apply the updates to an external database
204    /// and then start tracking a new set of updates.
205    ///
206    /// # Returns
207    ///
208    /// The accumulated updates, or an empty set if updates weren't being tracked.
209    fn take_updates(&mut self) -> SparseTrieUpdates;
210
211    /// Removes all nodes and values from the trie, resetting it to a blank state
212    /// with only an empty root node. This is used when a storage root is deleted.
213    ///
214    /// This should not be used when intending to reuse the trie for a fresh account/storage root;
215    /// use `clear` for that.
216    ///
217    /// Note: All previously tracked changes to the trie are also removed.
218    fn wipe(&mut self);
219
220    /// This clears all data structures in the sparse trie, keeping the backing data structures
221    /// allocated. A [`crate::SparseNode::Empty`] is inserted at the root.
222    ///
223    /// This is useful for reusing the trie without needing to reallocate memory.
224    fn clear(&mut self);
225}
226
227/// Struct for passing around branch node mask information.
228///
229/// Branch nodes can have up to 16 children (one for each nibble).
230/// The masks represent which children are stored in different ways:
231/// - `hash_mask`: Indicates which children are stored as hashes in the database
232/// - `tree_mask`: Indicates which children are complete subtrees stored in the database
233///
234/// These masks are essential for efficient trie traversal and serialization, as they
235/// determine how nodes should be encoded and stored on disk.
236#[derive(Debug, PartialEq, Eq, Clone, Copy)]
237pub struct TrieMasks {
238    /// Branch node hash mask, if any.
239    ///
240    /// When a bit is set, the corresponding child node's hash is stored in the trie.
241    ///
242    /// This mask enables selective hashing of child nodes.
243    pub hash_mask: Option<TrieMask>,
244    /// Branch node tree mask, if any.
245    ///
246    /// When a bit is set, the corresponding child subtree is stored in the database.
247    pub tree_mask: Option<TrieMask>,
248}
249
250impl TrieMasks {
251    /// Helper function, returns both fields `hash_mask` and `tree_mask` as [`None`]
252    pub const fn none() -> Self {
253        Self { hash_mask: None, tree_mask: None }
254    }
255}
256
257/// Tracks modifications to the sparse trie structure.
258///
259/// Maintains references to both modified and pruned/removed branches, enabling
260/// one to make batch updates to a persistent database.
261#[derive(Debug, Clone, Default, PartialEq, Eq)]
262pub struct SparseTrieUpdates {
263    /// Collection of updated intermediate nodes indexed by full path.
264    pub updated_nodes: HashMap<Nibbles, BranchNodeCompact>,
265    /// Collection of removed intermediate nodes indexed by full path.
266    pub removed_nodes: HashSet<Nibbles>,
267    /// Flag indicating whether the trie was wiped.
268    pub wiped: bool,
269}
270
271/// Error type for a leaf lookup operation
272#[derive(Debug, Clone, PartialEq, Eq)]
273pub enum LeafLookupError {
274    /// The path leads to a blinded node, cannot determine if leaf exists.
275    /// This means the witness is not complete.
276    BlindedNode {
277        /// Path to the blinded node.
278        path: Nibbles,
279        /// Hash of the blinded node.
280        hash: B256,
281    },
282    /// The path leads to a leaf with a different value than expected.
283    /// This means the witness is malformed.
284    ValueMismatch {
285        /// Path to the leaf.
286        path: Nibbles,
287        /// Expected value.
288        expected: Option<Vec<u8>>,
289        /// Actual value found.
290        actual: Vec<u8>,
291    },
292}
293
294/// Success value for a leaf lookup operation
295#[derive(Debug, Clone, PartialEq, Eq)]
296pub enum LeafLookup {
297    /// Leaf exists with expected value.
298    Exists,
299    /// Leaf does not exist (exclusion proof found).
300    NonExistent,
301}
302
303/// Carries all information needed by a sparse trie to reveal a particular node.
304#[derive(Debug, PartialEq, Eq)]
305pub struct RevealedSparseNode {
306    /// Path of the node.
307    pub path: Nibbles,
308    /// The node itself.
309    pub node: TrieNode,
310    /// Tree and hash masks for the node, if known.
311    pub masks: TrieMasks,
312}