mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
280 lines
9.8 KiB
Rust
280 lines
9.8 KiB
Rust
//! Traits for sparse trie implementations.
|
|
|
|
use core::fmt::Debug;
|
|
|
|
use alloc::{borrow::Cow, vec, vec::Vec};
|
|
use alloy_primitives::{
|
|
map::{HashMap, HashSet},
|
|
B256,
|
|
};
|
|
use alloy_trie::BranchNodeCompact;
|
|
use reth_execution_errors::SparseTrieResult;
|
|
use reth_trie_common::{BranchNodeMasks, Nibbles, ProofTrieNode, TrieNode};
|
|
|
|
use crate::provider::TrieNodeProvider;
|
|
|
|
/// Trait defining common operations for revealed sparse trie implementations.
|
|
///
|
|
/// This trait abstracts over different sparse trie implementations (serial vs parallel)
|
|
/// while providing a unified interface for the core trie operations needed by the
|
|
/// [`crate::SparseTrie`] enum.
|
|
pub trait SparseTrieInterface: Sized + Debug + Send + Sync {
|
|
/// Configures the trie to have the given root node revealed.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `root` - The root node to reveal
|
|
/// * `masks` - Trie masks for root branch node
|
|
/// * `retain_updates` - Whether to track updates
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Self if successful, or an error if revealing fails.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// May panic if the trie is not new/cleared, and has already revealed nodes.
|
|
fn with_root(
|
|
self,
|
|
root: TrieNode,
|
|
masks: Option<BranchNodeMasks>,
|
|
retain_updates: bool,
|
|
) -> SparseTrieResult<Self>;
|
|
|
|
/// Configures the trie to retain information about updates.
|
|
///
|
|
/// If `retain_updates` is true, the trie will record branch node updates
|
|
/// and deletions. This information can be used to efficiently update
|
|
/// an external database.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `retain_updates` - Whether to track updates
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Self for method chaining.
|
|
fn with_updates(self, retain_updates: bool) -> Self;
|
|
|
|
/// Reserves capacity for additional trie nodes.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `additional` - The number of additional trie nodes to reserve capacity for.
|
|
fn reserve_nodes(&mut self, _additional: usize) {}
|
|
|
|
/// The single-node version of `reveal_nodes`.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// `Ok(())` if successful, or an error if the node was not revealed.
|
|
fn reveal_node(
|
|
&mut self,
|
|
path: Nibbles,
|
|
node: TrieNode,
|
|
masks: Option<BranchNodeMasks>,
|
|
) -> SparseTrieResult<()> {
|
|
self.reveal_nodes(vec![ProofTrieNode { path, node, masks }])
|
|
}
|
|
|
|
/// Reveals one or more trie nodes if they have not been revealed before.
|
|
///
|
|
/// This function decodes trie nodes and inserts them into the trie structure. It handles
|
|
/// different node types (leaf, extension, branch) by appropriately adding them to the trie and
|
|
/// recursively revealing their children.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `nodes` - The nodes to be revealed, each having a path and optional set of branch node
|
|
/// masks. The nodes will be unsorted.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// `Ok(())` if successful, or an error if any of the nodes was not revealed.
|
|
fn reveal_nodes(&mut self, nodes: Vec<ProofTrieNode>) -> SparseTrieResult<()>;
|
|
|
|
/// Updates the value of a leaf node at the specified path.
|
|
///
|
|
/// If the leaf doesn't exist, it will be created.
|
|
/// If it does exist, its value will be updated.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `full_path` - The full path to the leaf
|
|
/// * `value` - The new value for the leaf
|
|
/// * `provider` - The trie provider for resolving missing nodes
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// `Ok(())` if successful, or an error if the update failed.
|
|
fn update_leaf<P: TrieNodeProvider>(
|
|
&mut self,
|
|
full_path: Nibbles,
|
|
value: Vec<u8>,
|
|
provider: P,
|
|
) -> SparseTrieResult<()>;
|
|
|
|
/// Removes a leaf node at the specified path.
|
|
///
|
|
/// This will also handle collapsing the trie structure as needed
|
|
/// (e.g., removing branch nodes that become unnecessary).
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `full_path` - The full path to the leaf to remove
|
|
/// * `provider` - The trie node provider for resolving missing nodes
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// `Ok(())` if successful, or an error if the removal failed.
|
|
fn remove_leaf<P: TrieNodeProvider>(
|
|
&mut self,
|
|
full_path: &Nibbles,
|
|
provider: P,
|
|
) -> SparseTrieResult<()>;
|
|
|
|
/// Calculates and returns the root hash of the trie.
|
|
///
|
|
/// This processes any dirty nodes by updating their RLP encodings
|
|
/// and returns the root hash.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The root hash of the trie.
|
|
fn root(&mut self) -> B256;
|
|
|
|
/// Recalculates and updates the RLP hashes of subtries deeper than a certain level. The level
|
|
/// is defined in the implementation.
|
|
///
|
|
/// The root node is considered to be at level 0. This method is useful for optimizing
|
|
/// hash recalculations after localized changes to the trie structure.
|
|
fn update_subtrie_hashes(&mut self);
|
|
|
|
/// Retrieves a reference to the leaf value at the specified path.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `full_path` - The full path to the leaf value
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A reference to the leaf value stored at the given full path, if it is revealed.
|
|
///
|
|
/// Note: a value can exist in the full trie and this function still returns `None`
|
|
/// because the value has not been revealed.
|
|
///
|
|
/// Hence a `None` indicates two possibilities:
|
|
/// - The value does not exists in the trie, so it cannot be revealed
|
|
/// - The value has not yet been revealed. In order to determine which is true, one would need
|
|
/// an exclusion proof.
|
|
fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>>;
|
|
|
|
/// Attempts to find a leaf node at the specified path.
|
|
///
|
|
/// This method traverses the trie from the root down to the given path, checking
|
|
/// if a leaf exists at that path. It can be used to verify the existence of a leaf
|
|
/// or to generate an exclusion proof (proof that a leaf does not exist).
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// - `full_path`: The path to search for.
|
|
/// - `expected_value`: Optional expected value. If provided, will verify the leaf value
|
|
/// matches.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// - `Ok(LeafLookup::Exists)` if the leaf exists with the expected value.
|
|
/// - `Ok(LeafLookup::NonExistent)` if the leaf definitely does not exist (exclusion proof).
|
|
/// - `Err(LeafLookupError)` if the search encountered a blinded node or found a different
|
|
/// value.
|
|
fn find_leaf(
|
|
&self,
|
|
full_path: &Nibbles,
|
|
expected_value: Option<&Vec<u8>>,
|
|
) -> Result<LeafLookup, LeafLookupError>;
|
|
|
|
/// Returns a reference to the current sparse trie updates.
|
|
///
|
|
/// If no updates have been made/recorded, returns an empty update set.
|
|
fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates>;
|
|
|
|
/// Consumes and returns the currently accumulated trie updates.
|
|
///
|
|
/// This is useful when you want to apply the updates to an external database
|
|
/// and then start tracking a new set of updates.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The accumulated updates, or an empty set if updates weren't being tracked.
|
|
fn take_updates(&mut self) -> SparseTrieUpdates;
|
|
|
|
/// Removes all nodes and values from the trie, resetting it to a blank state
|
|
/// with only an empty root node. This is used when a storage root is deleted.
|
|
///
|
|
/// This should not be used when intending to reuse the trie for a fresh account/storage root;
|
|
/// use `clear` for that.
|
|
///
|
|
/// Note: All previously tracked changes to the trie are also removed.
|
|
fn wipe(&mut self);
|
|
|
|
/// This clears all data structures in the sparse trie, keeping the backing data structures
|
|
/// allocated. A [`crate::SparseNode::Empty`] is inserted at the root.
|
|
///
|
|
/// This is useful for reusing the trie without needing to reallocate memory.
|
|
fn clear(&mut self);
|
|
|
|
/// Shrink the capacity of the sparse trie's node storage to the given size.
|
|
/// This will reduce memory usage if the current capacity is higher than the given size.
|
|
fn shrink_nodes_to(&mut self, size: usize);
|
|
|
|
/// Shrink the capacity of the sparse trie's value storage to the given size.
|
|
/// This will reduce memory usage if the current capacity is higher than the given size.
|
|
fn shrink_values_to(&mut self, size: usize);
|
|
}
|
|
|
|
/// Tracks modifications to the sparse trie structure.
|
|
///
|
|
/// Maintains references to both modified and pruned/removed branches, enabling
|
|
/// one to make batch updates to a persistent database.
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
pub struct SparseTrieUpdates {
|
|
/// Collection of updated intermediate nodes indexed by full path.
|
|
pub updated_nodes: HashMap<Nibbles, BranchNodeCompact>,
|
|
/// Collection of removed intermediate nodes indexed by full path.
|
|
pub removed_nodes: HashSet<Nibbles>,
|
|
/// Flag indicating whether the trie was wiped.
|
|
pub wiped: bool,
|
|
}
|
|
|
|
/// Error type for a leaf lookup operation
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum LeafLookupError {
|
|
/// The path leads to a blinded node, cannot determine if leaf exists.
|
|
/// This means the witness is not complete.
|
|
BlindedNode {
|
|
/// Path to the blinded node.
|
|
path: Nibbles,
|
|
/// Hash of the blinded node.
|
|
hash: B256,
|
|
},
|
|
/// The path leads to a leaf with a different value than expected.
|
|
/// This means the witness is malformed.
|
|
ValueMismatch {
|
|
/// Path to the leaf.
|
|
path: Nibbles,
|
|
/// Expected value.
|
|
expected: Option<Vec<u8>>,
|
|
/// Actual value found.
|
|
actual: Vec<u8>,
|
|
},
|
|
}
|
|
|
|
/// Success value for a leaf lookup operation
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum LeafLookup {
|
|
/// Leaf exists with expected value.
|
|
Exists,
|
|
/// Leaf does not exist (exclusion proof found).
|
|
NonExistent,
|
|
}
|