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}