mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
1 Commits
devnet4
...
dani/arena
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06878253fe |
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -1913,6 +1913,12 @@ dependencies = [
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boxcar"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e"
|
||||
|
||||
[[package]]
|
||||
name = "boyer-moore-magiclen"
|
||||
version = "0.2.22"
|
||||
@@ -5109,6 +5115,18 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inturn"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/DaniPopes/inturn?branch=dani%2Fcopy-interner#dfbf54fe098a323d326af9a6ecc8fd052af84587"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
"hashbrown 0.14.5",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.2"
|
||||
@@ -10674,6 +10692,7 @@ dependencies = [
|
||||
"arbitrary",
|
||||
"assert_matches",
|
||||
"auto_impl",
|
||||
"inturn",
|
||||
"itertools 0.14.0",
|
||||
"metrics",
|
||||
"pretty_assertions",
|
||||
|
||||
@@ -519,6 +519,7 @@ fdlimit = "0.3.0"
|
||||
fixed-map = { version = "0.9", default-features = false }
|
||||
humantime = "2.1"
|
||||
humantime-serde = "1.1"
|
||||
inturn = "0.1"
|
||||
itertools = { version = "0.14", default-features = false }
|
||||
linked_hash_set = "0.1"
|
||||
lz4 = "1.28.1"
|
||||
@@ -757,3 +758,5 @@ ipnet = "2.11"
|
||||
|
||||
# alloy-evm = { git = "https://github.com/alloy-rs/evm", rev = "072c248" }
|
||||
# alloy-op-evm = { git = "https://github.com/alloy-rs/evm", rev = "072c248" }
|
||||
|
||||
inturn = { git = "https://github.com/DaniPopes/inturn", branch = "dani/copy-interner" }
|
||||
|
||||
@@ -59,6 +59,7 @@ RUN --mount=type=secret,id=DEPOT_TOKEN,env=SCCACHE_WEBDAV_TOKEN \
|
||||
export RUSTFLAGS="-C target-cpu=x86-64-v3 -C target-feature=+pclmulqdq"; \
|
||||
fi && \
|
||||
cargo build --profile $BUILD_PROFILE --features "$FEATURES" --locked --bin $BINARY --manifest-path $MANIFEST_PATH/Cargo.toml && \
|
||||
pgrep -a sccache || echo "sccache server is NOT running (likely OOM-killed)" && \
|
||||
sccache --show-stats
|
||||
|
||||
# Copy binary to a known location (ARG not resolved in COPY)
|
||||
|
||||
@@ -25,6 +25,7 @@ alloy-rlp.workspace = true
|
||||
|
||||
# misc
|
||||
auto_impl.workspace = true
|
||||
inturn = { workspace = true, optional = true }
|
||||
rayon = { workspace = true, optional = true }
|
||||
smallvec.workspace = true
|
||||
|
||||
@@ -53,6 +54,7 @@ rand_08.workspace = true
|
||||
[features]
|
||||
default = ["std", "metrics"]
|
||||
std = [
|
||||
"dep:inturn",
|
||||
"dep:rayon",
|
||||
"alloy-primitives/std",
|
||||
"alloy-rlp/std",
|
||||
|
||||
245
crates/trie/sparse/src/interner.rs
Normal file
245
crates/trie/sparse/src/interner.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
//! Interned node storage for the parallel sparse trie.
|
||||
|
||||
use alloy_primitives::map::HashMap;
|
||||
use core::ops;
|
||||
pub use inturn;
|
||||
use inturn::{CopyInterner, Symbol};
|
||||
use reth_trie_common::Nibbles;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::SparseNode;
|
||||
|
||||
/// A shared path interner backed by [`CopyInterner<Nibbles>`].
|
||||
///
|
||||
/// This is a thread-safe, lock-free interner that deduplicates [`Nibbles`] paths across the entire
|
||||
/// [`crate::ParallelSparseTrie`]. Each unique path is assigned a compact [`Symbol`] index.
|
||||
/// The interner never frees symbols; it only grows. Node storage lifecycle is managed separately
|
||||
/// by each [`SparseNodeInterner`] via its `HashMap<Symbol, SparseNode>`.
|
||||
pub type SharedPathInterner = Arc<CopyInterner<Nibbles>>;
|
||||
|
||||
/// Creates a new shared path interner.
|
||||
pub fn shared_path_interner() -> SharedPathInterner {
|
||||
Arc::new(CopyInterner::new())
|
||||
}
|
||||
|
||||
/// An interned node store for [`SparseNode`]s keyed by [`Nibbles`] path.
|
||||
///
|
||||
/// Paths are interned into compact [`Symbol`]s via a [`SharedPathInterner`] that is shared across
|
||||
/// the entire [`crate::ParallelSparseTrie`] (upper subtrie + all 256 lower subtries).
|
||||
/// Nodes are stored in a `HashMap<Symbol, SparseNode>` per subtrie.
|
||||
pub struct SparseNodeInterner {
|
||||
/// Shared path interner.
|
||||
interner: SharedPathInterner,
|
||||
/// Nodes stored by interned symbol.
|
||||
nodes: HashMap<Symbol, SparseNode>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for SparseNodeInterner {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_map().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SparseNodeInterner {
|
||||
fn clone(&self) -> Self {
|
||||
Self { interner: Arc::clone(&self.interner), nodes: self.nodes.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SparseNodeInterner {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.nodes.len() != other.nodes.len() {
|
||||
return false;
|
||||
}
|
||||
for (path, node) in self.iter() {
|
||||
match other.get(path) {
|
||||
Some(other_node) if node == other_node => {}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SparseNodeInterner {}
|
||||
|
||||
impl SparseNodeInterner {
|
||||
/// Creates an empty interner backed by the given shared path interner.
|
||||
pub fn new(interner: SharedPathInterner) -> Self {
|
||||
Self { interner, nodes: HashMap::default() }
|
||||
}
|
||||
|
||||
/// Returns a reference to the shared path interner.
|
||||
pub const fn interner(&self) -> &SharedPathInterner {
|
||||
&self.interner
|
||||
}
|
||||
|
||||
/// Returns the number of live entries.
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if there are no live entries.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.nodes.is_empty()
|
||||
}
|
||||
|
||||
/// Reserves capacity for at least `additional` more entries.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.nodes.reserve(additional);
|
||||
}
|
||||
|
||||
/// Shrinks the capacity of the underlying storage.
|
||||
pub fn shrink_to(&mut self, min_capacity: usize) {
|
||||
self.nodes.shrink_to(min_capacity);
|
||||
}
|
||||
|
||||
/// Gets a reference to the node at the given path.
|
||||
pub fn get(&self, path: &Nibbles) -> Option<&SparseNode> {
|
||||
let sym = self.interner.intern(path);
|
||||
self.nodes.get(&sym)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the node at the given path.
|
||||
pub fn get_mut(&mut self, path: &Nibbles) -> Option<&mut SparseNode> {
|
||||
let sym = self.interner.intern(path);
|
||||
self.nodes.get_mut(&sym)
|
||||
}
|
||||
|
||||
/// Returns `true` if the path has a live node.
|
||||
pub fn contains_key(&self, path: &Nibbles) -> bool {
|
||||
let sym = self.interner.intern(path);
|
||||
self.nodes.contains_key(&sym)
|
||||
}
|
||||
|
||||
/// Inserts a node at the given path, returning the previous node if any.
|
||||
pub fn insert(&mut self, path: Nibbles, node: SparseNode) -> Option<SparseNode> {
|
||||
let sym = self.interner.intern(&path);
|
||||
self.nodes.insert(sym, node)
|
||||
}
|
||||
|
||||
/// Removes the node at the given path, returning it if it existed.
|
||||
pub fn remove(&mut self, path: &Nibbles) -> Option<SparseNode> {
|
||||
let sym = self.interner.intern(path);
|
||||
self.nodes.remove(&sym)
|
||||
}
|
||||
|
||||
/// Provides entry API similar to [`HashMap::entry`].
|
||||
pub fn entry(&mut self, path: Nibbles) -> Entry<'_> {
|
||||
let sym = self.interner.intern(&path);
|
||||
match self.nodes.entry(sym) {
|
||||
alloy_primitives::map::Entry::Occupied(e) => {
|
||||
Entry::Occupied(OccupiedEntry { path, entry: e })
|
||||
}
|
||||
alloy_primitives::map::Entry::Vacant(e) => {
|
||||
Entry::Vacant(VacantEntry { path, entry: e })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retains only the entries for which the predicate returns `true`.
|
||||
pub fn retain(&mut self, mut f: impl FnMut(&Nibbles, &mut SparseNode) -> bool) {
|
||||
let interner = &self.interner;
|
||||
self.nodes.retain(|sym, node| {
|
||||
let path = interner.resolve(*sym);
|
||||
f(path, node)
|
||||
});
|
||||
}
|
||||
|
||||
/// Clears all entries, keeping allocations.
|
||||
pub fn clear(&mut self) {
|
||||
self.nodes.clear();
|
||||
}
|
||||
|
||||
/// Iterates over all live `(path, node)` pairs.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Nibbles, &SparseNode)> {
|
||||
self.nodes.iter().map(|(sym, node)| (self.interner.resolve(*sym), node))
|
||||
}
|
||||
|
||||
/// Returns a heuristic for the in-memory size in bytes.
|
||||
pub fn memory_size(&self) -> usize {
|
||||
let mut size = core::mem::size_of::<Self>();
|
||||
for (path, node) in self.iter() {
|
||||
size += core::mem::size_of::<Symbol>() + core::mem::size_of::<SparseNode>();
|
||||
size += path.len();
|
||||
size += node.memory_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry API for [`SparseNodeInterner`].
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub enum Entry<'a> {
|
||||
/// An occupied entry.
|
||||
Occupied(OccupiedEntry<'a>),
|
||||
/// A vacant entry.
|
||||
Vacant(VacantEntry<'a>),
|
||||
}
|
||||
|
||||
/// An occupied entry in the [`SparseNodeInterner`].
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct OccupiedEntry<'a> {
|
||||
path: Nibbles,
|
||||
entry: alloy_primitives::map::OccupiedEntry<'a, Symbol, SparseNode>,
|
||||
}
|
||||
|
||||
impl<'a> OccupiedEntry<'a> {
|
||||
/// Returns a reference to the key.
|
||||
pub const fn key(&self) -> &Nibbles {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Returns a reference to the value.
|
||||
pub fn get(&self) -> &SparseNode {
|
||||
self.entry.get()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value.
|
||||
pub fn get_mut(&mut self) -> &mut SparseNode {
|
||||
self.entry.get_mut()
|
||||
}
|
||||
|
||||
/// Replaces the value, returning the old one.
|
||||
pub fn insert(&mut self, node: SparseNode) -> SparseNode {
|
||||
self.entry.insert(node)
|
||||
}
|
||||
|
||||
/// Converts into a mutable reference to the value.
|
||||
pub fn into_mut(self) -> &'a mut SparseNode {
|
||||
self.entry.into_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for OccupiedEntry<'_> {
|
||||
type Target = SparseNode;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.entry.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DerefMut for OccupiedEntry<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.entry.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// A vacant entry in the [`SparseNodeInterner`].
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct VacantEntry<'a> {
|
||||
path: Nibbles,
|
||||
entry: alloy_primitives::map::VacantEntry<'a, Symbol, SparseNode>,
|
||||
}
|
||||
|
||||
impl<'a> VacantEntry<'a> {
|
||||
/// Returns a reference to the key.
|
||||
pub const fn key(&self) -> &Nibbles {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Inserts a value and returns a mutable reference to it.
|
||||
pub fn insert(self, node: SparseNode) -> &'a mut SparseNode {
|
||||
self.entry.insert(node)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ pub use parallel::*;
|
||||
|
||||
mod lower;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod interner;
|
||||
|
||||
pub mod provider;
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
|
||||
@@ -47,7 +47,11 @@ impl LowerSparseSubtrie {
|
||||
/// The given path is the path of a node which will be set into the [`SparseSubtrie`]'s `nodes`
|
||||
/// map immediately upon being revealed. If the subtrie is blinded, or if its current root path
|
||||
/// is longer than this one, than this one becomes the new root path of the subtrie.
|
||||
pub(crate) fn reveal(&mut self, path: &Nibbles) {
|
||||
pub(crate) fn reveal(
|
||||
&mut self,
|
||||
path: &Nibbles,
|
||||
interner: &crate::interner::SharedPathInterner,
|
||||
) {
|
||||
match self {
|
||||
Self::Blind(allocated) => {
|
||||
debug_assert!(allocated.as_ref().is_none_or(|subtrie| subtrie.is_empty()));
|
||||
@@ -55,7 +59,10 @@ impl LowerSparseSubtrie {
|
||||
subtrie.path = *path;
|
||||
Self::Revealed(subtrie)
|
||||
} else {
|
||||
Self::Revealed(Box::new(SparseSubtrie::new(*path)))
|
||||
Self::Revealed(Box::new(SparseSubtrie::new(
|
||||
*path,
|
||||
std::sync::Arc::clone(interner),
|
||||
)))
|
||||
}
|
||||
}
|
||||
Self::Revealed(subtrie) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
interner::{shared_path_interner, SharedPathInterner},
|
||||
lower::LowerSparseSubtrie,
|
||||
provider::{RevealedNode, TrieNodeProvider},
|
||||
LeafLookup, LeafLookupError, RlpNodeStackItem, SparseNode, SparseNodeType, SparseTrie,
|
||||
@@ -19,6 +20,7 @@ use reth_trie_common::{
|
||||
ProofTrieNode, RlpNode, TrieNode,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
/// The maximum length of a path, in nibbles, which belongs to the upper subtrie of a
|
||||
@@ -101,8 +103,10 @@ pub struct ParallelismThresholds {
|
||||
/// - Each leaf entry in the `subtries` and `upper_trie` collection must have a corresponding entry
|
||||
/// in `values` collection. If the root node is a leaf, it must also have an entry in `values`.
|
||||
/// - All keys in `values` collection are full leaf paths.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct ParallelSparseTrie {
|
||||
/// Shared path interner used across upper and lower subtries.
|
||||
path_interner: SharedPathInterner,
|
||||
/// This contains the trie nodes for the upper part of the trie.
|
||||
upper_subtrie: Box<SparseSubtrie>,
|
||||
/// An array containing the subtries at the second level of the trie.
|
||||
@@ -133,11 +137,14 @@ pub struct ParallelSparseTrie {
|
||||
|
||||
impl Default for ParallelSparseTrie {
|
||||
fn default() -> Self {
|
||||
let path_interner = shared_path_interner();
|
||||
Self {
|
||||
upper_subtrie: Box::new(SparseSubtrie {
|
||||
nodes: HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]),
|
||||
..Default::default()
|
||||
}),
|
||||
upper_subtrie: {
|
||||
let mut subtrie = SparseSubtrie::with_interner(Arc::clone(&path_interner));
|
||||
subtrie.nodes.insert(Nibbles::default(), SparseNode::Empty);
|
||||
Box::new(subtrie)
|
||||
},
|
||||
path_interner,
|
||||
lower_subtries: Box::new(
|
||||
[const { LowerSparseSubtrie::Blind(None) }; NUM_LOWER_SUBTRIES],
|
||||
),
|
||||
@@ -153,6 +160,36 @@ impl Default for ParallelSparseTrie {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ParallelSparseTrie {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.upper_subtrie == other.upper_subtrie &&
|
||||
self.lower_subtries == other.lower_subtries &&
|
||||
self.prefix_set == other.prefix_set &&
|
||||
self.updates == other.updates &&
|
||||
self.branch_node_masks == other.branch_node_masks &&
|
||||
self.update_actions_buffers == other.update_actions_buffers &&
|
||||
self.parallelism_thresholds == other.parallelism_thresholds &&
|
||||
self.subtrie_heat == other.subtrie_heat
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ParallelSparseTrie {}
|
||||
|
||||
impl core::fmt::Debug for ParallelSparseTrie {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("ParallelSparseTrie")
|
||||
.field("upper_subtrie", &self.upper_subtrie)
|
||||
.field("lower_subtries", &self.lower_subtries)
|
||||
.field("prefix_set", &self.prefix_set)
|
||||
.field("updates", &self.updates)
|
||||
.field("branch_node_masks", &self.branch_node_masks)
|
||||
.field("update_actions_buffers", &self.update_actions_buffers)
|
||||
.field("parallelism_thresholds", &self.parallelism_thresholds)
|
||||
.field("subtrie_heat", &self.subtrie_heat)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SparseTrie for ParallelSparseTrie {
|
||||
fn set_root(
|
||||
&mut self,
|
||||
@@ -242,7 +279,7 @@ impl SparseTrie for ParallelSparseTrie {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
self.lower_subtries[idx].reveal(&node.path);
|
||||
self.lower_subtries[idx].reveal(&node.path, &self.path_interner);
|
||||
self.subtrie_heat.mark_modified(idx);
|
||||
self.lower_subtries[idx]
|
||||
.as_revealed_mut()
|
||||
@@ -302,7 +339,7 @@ impl SparseTrie for ParallelSparseTrie {
|
||||
// the first element of each group, the `path` here will necessarily be the
|
||||
// shortest path being revealed for each subtrie. Therefore we can reveal the
|
||||
// subtrie itself using this path and retain correct behavior.
|
||||
self.lower_subtries[idx].reveal(&node.path);
|
||||
self.lower_subtries[idx].reveal(&node.path, &self.path_interner);
|
||||
Some((idx, self.lower_subtries[idx].take_revealed().expect("just revealed")))
|
||||
})
|
||||
.collect();
|
||||
@@ -1530,7 +1567,7 @@ impl ParallelSparseTrie {
|
||||
match SparseSubtrieType::from_path(path) {
|
||||
SparseSubtrieType::Upper => None,
|
||||
SparseSubtrieType::Lower(idx) => {
|
||||
self.lower_subtries[idx].reveal(path);
|
||||
self.lower_subtries[idx].reveal(path, &self.path_interner);
|
||||
self.subtrie_heat.mark_modified(idx);
|
||||
Some(self.lower_subtries[idx].as_revealed_mut().expect("just revealed"))
|
||||
}
|
||||
@@ -2298,7 +2335,7 @@ impl ParallelSparseTrie {
|
||||
/// This is used for leaves that sit at the upper/lower subtrie boundary, where the leaf is
|
||||
/// in a lower subtrie but its parent branch is in the upper subtrie.
|
||||
fn is_boundary_leaf_reachable(
|
||||
upper_nodes: &HashMap<Nibbles, SparseNode>,
|
||||
upper_nodes: &crate::interner::SparseNodeInterner,
|
||||
path: &Nibbles,
|
||||
node: &TrieNode,
|
||||
) -> bool {
|
||||
@@ -2440,7 +2477,7 @@ impl SubtrieModifications {
|
||||
|
||||
/// This is a subtrie of the [`ParallelSparseTrie`] that contains a map from path to sparse trie
|
||||
/// nodes.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct SparseSubtrie {
|
||||
/// The root path of this subtrie.
|
||||
///
|
||||
@@ -2450,8 +2487,8 @@ pub struct SparseSubtrie {
|
||||
///
|
||||
/// There should be a node for this path in `nodes` map.
|
||||
pub(crate) path: Nibbles,
|
||||
/// The map from paths to sparse trie nodes within this subtrie.
|
||||
nodes: HashMap<Nibbles, SparseNode>,
|
||||
/// Interned map from paths to sparse trie nodes within this subtrie.
|
||||
nodes: crate::interner::SparseNodeInterner,
|
||||
/// Subset of fields for mutable access while `nodes` field is also being mutably borrowed.
|
||||
inner: SparseSubtrieInner,
|
||||
}
|
||||
@@ -2473,8 +2510,17 @@ enum FindNextToLeafOutcome {
|
||||
|
||||
impl SparseSubtrie {
|
||||
/// Creates a new empty subtrie with the specified root path.
|
||||
pub(crate) fn new(path: Nibbles) -> Self {
|
||||
Self { path, ..Default::default() }
|
||||
pub(crate) fn new(path: Nibbles, interner: SharedPathInterner) -> Self {
|
||||
Self {
|
||||
path,
|
||||
nodes: crate::interner::SparseNodeInterner::new(interner),
|
||||
inner: SparseSubtrieInner::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new empty subtrie with the default root path.
|
||||
pub(crate) fn with_interner(interner: SharedPathInterner) -> Self {
|
||||
Self::new(Nibbles::default(), interner)
|
||||
}
|
||||
|
||||
/// Returns true if this subtrie has any nodes, false otherwise.
|
||||
@@ -2840,7 +2886,7 @@ impl SparseSubtrie {
|
||||
// Update the branch node entry in the nodes map, handling cases where a blinded
|
||||
// node is now replaced with a revealed node.
|
||||
match self.nodes.entry(path) {
|
||||
Entry::Occupied(mut entry) => match entry.get() {
|
||||
crate::interner::Entry::Occupied(mut entry) => match entry.get() {
|
||||
// Replace a hash node with a fully revealed branch node.
|
||||
SparseNode::Hash(hash) => {
|
||||
entry.insert(SparseNode::Branch {
|
||||
@@ -2855,7 +2901,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
_ => unreachable!("checked that node is either a hash or non-existent"),
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
crate::interner::Entry::Vacant(entry) => {
|
||||
entry.insert(SparseNode::new_branch(branch.state_mask));
|
||||
}
|
||||
}
|
||||
@@ -2875,7 +2921,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
}
|
||||
TrieNode::Extension(ext) => match self.nodes.entry(path) {
|
||||
Entry::Occupied(mut entry) => match entry.get() {
|
||||
crate::interner::Entry::Occupied(mut entry) => match entry.get() {
|
||||
// Replace a hash node with a revealed extension node.
|
||||
SparseNode::Hash(hash) => {
|
||||
let mut child_path = *entry.key();
|
||||
@@ -2893,7 +2939,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
_ => unreachable!("checked that node is either a hash or non-existent"),
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
crate::interner::Entry::Vacant(entry) => {
|
||||
let mut child_path = *entry.key();
|
||||
child_path.extend(&ext.key);
|
||||
entry.insert(SparseNode::new_ext(ext.key));
|
||||
@@ -2936,7 +2982,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
|
||||
match self.nodes.entry(path) {
|
||||
Entry::Occupied(mut entry) => match entry.get() {
|
||||
crate::interner::Entry::Occupied(mut entry) => match entry.get() {
|
||||
// Replace a hash node with a revealed leaf node and store leaf node value.
|
||||
SparseNode::Hash(hash) => {
|
||||
entry.insert(SparseNode::Leaf {
|
||||
@@ -2948,7 +2994,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
_ => unreachable!("checked that node is either a hash or non-existent"),
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
crate::interner::Entry::Vacant(entry) => {
|
||||
entry.insert(SparseNode::new_leaf(leaf.key));
|
||||
}
|
||||
}
|
||||
@@ -2980,7 +3026,7 @@ impl SparseSubtrie {
|
||||
if child.len() == B256::len_bytes() + 1 {
|
||||
let hash = B256::from_slice(&child[1..]);
|
||||
match self.nodes.entry(path) {
|
||||
Entry::Occupied(entry) => match entry.get() {
|
||||
crate::interner::Entry::Occupied(entry) => match entry.get() {
|
||||
// Hash node with a different hash can't be handled.
|
||||
SparseNode::Hash(previous_hash) if previous_hash != &hash => {
|
||||
return Err(SparseTrieErrorKind::Reveal {
|
||||
@@ -2991,7 +3037,7 @@ impl SparseSubtrie {
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Entry::Vacant(entry) => {
|
||||
crate::interner::Entry::Vacant(entry) => {
|
||||
entry.insert(SparseNode::Hash(hash));
|
||||
}
|
||||
}
|
||||
@@ -3058,7 +3104,8 @@ impl SparseSubtrie {
|
||||
/// Removes all nodes and values from the subtrie, resetting it to a blank state
|
||||
/// with only an empty root node. This is used when a storage root is deleted.
|
||||
fn wipe(&mut self) {
|
||||
self.nodes = HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]);
|
||||
self.nodes.clear();
|
||||
self.nodes.insert(Nibbles::default(), SparseNode::Empty);
|
||||
self.inner.clear();
|
||||
}
|
||||
|
||||
@@ -3082,12 +3129,8 @@ impl SparseSubtrie {
|
||||
pub(crate) fn memory_size(&self) -> usize {
|
||||
let mut size = core::mem::size_of::<Self>();
|
||||
|
||||
// Nodes map: key (Nibbles) + value (SparseNode)
|
||||
for (path, node) in &self.nodes {
|
||||
size += core::mem::size_of::<Nibbles>();
|
||||
size += path.len(); // Nibbles heap allocation
|
||||
size += node.memory_size();
|
||||
}
|
||||
// Nodes interner
|
||||
size += self.nodes.memory_size();
|
||||
|
||||
// Values map: key (Nibbles) + value (Vec<u8>)
|
||||
for (path, value) in &self.inner.values {
|
||||
@@ -3689,6 +3732,7 @@ mod tests {
|
||||
SparseSubtrieType,
|
||||
};
|
||||
use crate::{
|
||||
interner::shared_path_interner,
|
||||
parallel::ChangedSubtrie,
|
||||
provider::{DefaultTrieNodeProvider, RevealedNode, TrieNodeProvider},
|
||||
LeafLookup, LeafLookupError, SparseNode, SparseTrie, SparseTrieUpdates,
|
||||
@@ -3722,7 +3766,10 @@ mod tests {
|
||||
ProofTrieNode, RlpNode, TrieMask, TrieNode, EMPTY_ROOT_HASH,
|
||||
};
|
||||
use reth_trie_db::DatabaseTrieCursorFactory;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Pad nibbles to the length of a B256 hash with zeros on the right.
|
||||
fn pad_nibbles_right(mut nibbles: Nibbles) -> Nibbles {
|
||||
@@ -4165,11 +4212,15 @@ mod tests {
|
||||
fn test_get_changed_subtries() {
|
||||
// Create a trie with three subtries
|
||||
let mut trie = ParallelSparseTrie::default();
|
||||
let subtrie_1 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
|
||||
let interner = shared_path_interner();
|
||||
let subtrie_1 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_1_index = path_subtrie_index_unchecked(&subtrie_1.path);
|
||||
let subtrie_2 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0])));
|
||||
let subtrie_2 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_2_index = path_subtrie_index_unchecked(&subtrie_2.path);
|
||||
let subtrie_3 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0])));
|
||||
let subtrie_3 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_3_index = path_subtrie_index_unchecked(&subtrie_3.path);
|
||||
|
||||
// Add subtries at specific positions
|
||||
@@ -4219,11 +4270,15 @@ mod tests {
|
||||
fn test_get_changed_subtries_all() {
|
||||
// Create a trie with three subtries
|
||||
let mut trie = ParallelSparseTrie::default();
|
||||
let subtrie_1 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
|
||||
let interner = shared_path_interner();
|
||||
let subtrie_1 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_1_index = path_subtrie_index_unchecked(&subtrie_1.path);
|
||||
let subtrie_2 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0])));
|
||||
let subtrie_2 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x1, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_2_index = path_subtrie_index_unchecked(&subtrie_2.path);
|
||||
let subtrie_3 = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0])));
|
||||
let subtrie_3 =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x3, 0x0]), Arc::clone(&interner)));
|
||||
let subtrie_3_index = path_subtrie_index_unchecked(&subtrie_3.path);
|
||||
|
||||
// Add subtries at specific positions
|
||||
@@ -4622,7 +4677,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_subtrie_update_hashes() {
|
||||
let mut subtrie = Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0])));
|
||||
let mut subtrie =
|
||||
Box::new(SparseSubtrie::new(Nibbles::from_nibbles([0x0, 0x0]), shared_path_interner()));
|
||||
|
||||
// Create leaf nodes with paths 0x0...0, 0x00001...0, 0x0010...0
|
||||
let leaf_1_full_path = Nibbles::from_nibbles([0; 64]);
|
||||
|
||||
Reference in New Issue
Block a user