Compare commits

...

17 Commits

Author SHA1 Message Date
Arsenii Kulikov
450713e79d rm file 2026-02-19 18:02:11 +04:00
Arsenii Kulikov
699a26ea1e rm span per trie 2026-02-19 17:47:33 +04:00
Arsenii Kulikov
955976dd63 rm address 2026-02-19 17:15:18 +04:00
Arsenii Kulikov
b37151c6a2 sequential storage tries 2026-02-19 16:12:52 +04:00
Arsenii Kulikov
47b59e8ebc instrument 2026-02-19 15:56:22 +04:00
Arsenii Kulikov
9af31bc2c4 instrument 2026-02-19 15:51:02 +04:00
Arsenii Kulikov
2c8a66ef29 Merge branch 'klkvr/optimize-find-leaf' into klkvr/bench 2026-02-19 15:22:08 +04:00
Arsenii Kulikov
ecf640d33e wip 2026-02-19 13:57:54 +04:00
Arsenii Kulikov
245b499b19 wip 2026-02-19 02:54:51 +04:00
Arsenii Kulikov
170d0a5e52 don't try fetching parent upper node for unreachable subtrie roots 2026-02-18 19:43:28 +04:00
Arsenii Kulikov
bba57395c6 delay alloc 2026-02-18 18:57:44 +04:00
Arsenii Kulikov
ca62b86733 hash_from_upper 2026-02-18 18:55:48 +04:00
Alexey Shekhirin
102acb7bf0 Merge branch 'main' into klkvr/hashes-on-branch 2026-02-18 12:31:17 +00:00
Arsenii Kulikov
0119a880b1 clippy 2026-02-18 16:25:10 +04:00
Arsenii Kulikov
e0ed7e0d4b fix tests 2026-02-18 16:17:36 +04:00
Arsenii Kulikov
4d3ed18a9e rm Hash variant 2026-02-18 00:09:01 +04:00
Arsenii Kulikov
f215ab39a2 wip 2026-02-17 22:23:05 +04:00
7 changed files with 77 additions and 52 deletions

3
Cargo.lock generated
View File

@@ -6239,8 +6239,7 @@ dependencies = [
[[package]]
name = "nybbles"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210"
source = "git+https://github.com/alloy-rs/nybbles?rev=8df38fb#8df38fb3f9c897090771a56a639eaf161327109e"
dependencies = [
"alloy-rlp",
"arbitrary",

View File

@@ -760,3 +760,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" }
nybbles = { git = "https://github.com/alloy-rs/nybbles", rev = "8df38fb" }

View File

@@ -466,49 +466,36 @@ where
if new { &mut self.new_storage_updates } else { &mut self.storage_updates };
// Process all storage updates in parallel, skipping tries with no pending updates.
let span = tracing::Span::current();
let storage_results = storage_updates
.iter_mut()
.filter(|(_, updates)| !updates.is_empty())
.map(|(address, updates)| {
let trie = self.trie.take_or_create_storage_trie(address);
let fetched = self.fetched_storage_targets.remove(address).unwrap_or_default();
let span = debug_span!("process_storage_leaf_updates").entered();
for (address, updates) in storage_updates {
if updates.is_empty() {
continue;
}
(address, updates, fetched, trie)
})
.par_bridge_buffered()
.map(|(address, updates, mut fetched, mut trie)| {
let _enter = debug_span!(target: "engine::tree::payload_processor::sparse_trie", parent: &span, "storage_trie_leaf_updates", a=%address).entered();
let mut targets = Vec::new();
let trie = self.trie.get_or_create_storage_trie_mut(*address);
let fetched = self.fetched_storage_targets.entry(*address).or_default();
let mut targets = Vec::new();
trie.update_leaves(updates, |path, min_len| match fetched.entry(path) {
Entry::Occupied(mut entry) => {
if min_len < *entry.get() {
entry.insert(min_len);
targets.push(Target::new(path).with_min_len(min_len));
}
}
Entry::Vacant(entry) => {
trie.update_leaves(updates, |path, min_len| match fetched.entry(path) {
Entry::Occupied(mut entry) => {
if min_len < *entry.get() {
entry.insert(min_len);
targets.push(Target::new(path).with_min_len(min_len));
}
})?;
SparseTrieResult::Ok((address, targets, fetched, trie))
})
.collect::<Result<Vec<_>, _>>()?;
drop(span);
for (address, targets, fetched, trie) in storage_results {
self.fetched_storage_targets.insert(*address, fetched);
self.trie.insert_storage_trie(*address, trie);
}
Entry::Vacant(entry) => {
entry.insert(min_len);
targets.push(Target::new(path).with_min_len(min_len));
}
})?;
if !targets.is_empty() {
self.pending_targets.storage_targets.entry(*address).or_default().extend(targets);
}
}
drop(span);
// Process account trie updates and fill the account targets.
self.process_account_leaf_updates(new)?;
@@ -577,7 +564,7 @@ where
})
.par_bridge_buffered()
.for_each(|(address, trie)| {
let _enter = debug_span!(target: "engine::tree::payload_processor::sparse_trie", parent: &span, "storage_root", ?address).entered();
let _enter = debug_span!(target: "engine::tree::payload_processor::sparse_trie", parent: &span, "storage_root", ?address, prefix_set_len = trie.prefix_set_len()).entered();
trie.root().expect("updates are drained, trie should be revealed by now");
});
drop(span);

View File

@@ -931,6 +931,10 @@ impl SparseTrie for ParallelSparseTrie {
.is_some_and(|node| node.cached_rlp_node().is_some())
}
fn prefix_set_len(&self) -> usize {
self.prefix_set.len()
}
#[instrument(level = "trace", target = "trie::sparse::parallel", skip(self))]
fn update_subtrie_hashes(&mut self) {
trace!(target: "trie::parallel_sparse", "Updating subtrie hashes");
@@ -1099,25 +1103,48 @@ impl SparseTrie for ParallelSparseTrie {
loop {
let curr_node = curr_subtrie.nodes.get(&curr_path).unwrap();
match Self::find_next_to_leaf(&curr_path, curr_node, full_path) {
FindNextToLeafOutcome::NotFound => return Ok(LeafLookup::NonExistent),
FindNextToLeafOutcome::BlindedNode { path, hash } => {
return Err(LeafLookupError::BlindedNode { path, hash });
match curr_node {
SparseNode::Empty => return Ok(LeafLookup::NonExistent),
SparseNode::Leaf { key, .. } => {
let mut found_full_path = curr_path;
found_full_path.extend(key);
assert!(&found_full_path != full_path, "target leaf {full_path:?} found, even though value wasn't in values hashmap");
return Ok(LeafLookup::NonExistent)
}
FindNextToLeafOutcome::Found => {
panic!("target leaf {full_path:?} found at path {curr_path:?}, even though value wasn't in values hashmap");
}
FindNextToLeafOutcome::ContinueFrom(next_path) => {
curr_path = next_path;
// If we were previously looking at the upper trie, and the new path is in the
// lower trie, we need to pull out a ref to the lower trie.
if curr_subtrie_is_upper &&
let Some(lower_subtrie) = self.lower_subtrie_for_path(&curr_path)
{
curr_subtrie = lower_subtrie;
curr_subtrie_is_upper = false;
SparseNode::Extension { key, .. } => {
if full_path.len() == curr_path.len() {
return Ok(LeafLookup::NonExistent)
}
curr_path.extend(key);
if !full_path.starts_with(&curr_path) {
return Ok(LeafLookup::NonExistent)
}
}
SparseNode::Branch { state_mask, blinded_mask, blinded_hashes, .. } => {
if full_path.len() == curr_path.len() {
return Ok(LeafLookup::NonExistent)
}
let nibble = full_path.get_unchecked(curr_path.len());
if !state_mask.is_bit_set(nibble) {
return Ok(LeafLookup::NonExistent)
}
curr_path.push_unchecked(nibble);
if blinded_mask.is_bit_set(nibble) {
return Err(LeafLookupError::BlindedNode {
path: curr_path,
hash: blinded_hashes[nibble as usize],
})
}
}
}
// If we were previously looking at the upper trie, and the new path is in the
// lower trie, we need to pull out a ref to the lower trie.
if curr_subtrie_is_upper &&
let Some(lower_subtrie) = self.lower_subtrie_for_path(&curr_path)
{
curr_subtrie = lower_subtrie;
curr_subtrie_is_upper = false;
}
}
}

View File

@@ -22,7 +22,7 @@ use reth_trie_common::{
};
#[cfg(feature = "std")]
use tracing::debug;
use tracing::{instrument, trace};
use tracing::{debug_span, instrument, trace};
/// Holds data that should be dropped after any locks are released.
///
@@ -323,6 +323,7 @@ where
Ok(())
}
let _span = debug_span!("reveal_storage_v2_proof_nodes").entered();
#[cfg(feature = "std")]
// If std then reveal storage proofs in parallel
{
@@ -389,6 +390,7 @@ where
///
/// V2 proofs already include the masks in the `ProofTrieNode` structure,
/// so no separate masks map is needed.
#[instrument(level = "debug", target = "trie::sparse", skip_all)]
pub fn reveal_account_v2_proof_nodes(
&mut self,
mut nodes: Vec<ProofTrieNodeV2>,

View File

@@ -193,6 +193,9 @@ pub trait SparseTrie: Sized + Debug + Send + Sync {
/// Returns true if the root node is cached and does not need any recomputation.
fn is_root_cached(&self) -> bool;
/// Returns the length of the prefix set of the trie.
fn prefix_set_len(&self) -> usize;
/// Recalculates and updates the RLP hashes of subtries deeper than a certain level. The level
/// is defined in the implementation.
///

View File

@@ -169,6 +169,11 @@ impl<T: SparseTrieTrait> RevealableSparseTrie<T> {
self.as_revealed_ref().is_some_and(|trie| trie.is_root_cached())
}
/// Returns the length of the prefix set of the trie.
pub fn prefix_set_len(&self) -> usize {
self.as_revealed_ref().map(|trie| trie.prefix_set_len()).unwrap_or(0)
}
/// Returns the root hash along with any accumulated update information.
///
/// This is useful for when you need both the root hash and information about