Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/engine/tree/src/tree/payload_processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ where
proof_worker_handle,
trie_metrics.clone(),
sparse_state_trie,
parent_state_root,
chunk_size,
);

Expand Down
76 changes: 70 additions & 6 deletions crates/engine/tree/src/tree/payload_processor/sparse_trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ use reth_trie_parallel::{
root::ParallelStateRootError,
};
use reth_trie_sparse::{
errors::SparseTrieResult, ConfigurableSparseTrie, DeferredDrops, LeafUpdate,
RevealableSparseTrie, SparseStateTrie, SparseTrie,
errors::{SparseStateTrieErrorKind, SparseTrieErrorKind, SparseTrieResult},
ConfigurableSparseTrie, DeferredDrops, LeafUpdate, RevealableSparseTrie, SparseStateTrie,
SparseTrie,
};
use revm_primitives::{hash_map::Entry, B256Map};
use tracing::{debug, debug_span, error, instrument, trace_span};
Expand All @@ -46,6 +47,8 @@ pub(super) struct SparseTrieCacheTask<A = ConfigurableSparseTrie, S = Configurab
updates: CrossbeamReceiver<SparseTrieTaskMessage>,
/// `SparseStateTrie` used for computing the state root.
trie: SparseStateTrie<A, S>,
/// The parent block's state root.
parent_state_root: B256,
/// Handle to the proof worker pools (storage and account).
proof_worker_handle: ProofWorkerHandle,

Expand Down Expand Up @@ -120,6 +123,7 @@ where
proof_worker_handle: ProofWorkerHandle,
metrics: MultiProofTaskMetrics,
trie: SparseStateTrie<A, S>,
parent_state_root: B256,
chunk_size: usize,
) -> Self {
let (proof_result_tx, proof_result_rx) = crossbeam_channel::unbounded();
Expand All @@ -138,6 +142,7 @@ where
updates: hashed_state_rx,
proof_worker_handle,
trie,
parent_state_root,
chunk_size,
max_targets_for_chunking: DEFAULT_MAX_TARGETS_FOR_CHUNKING,
account_updates: Default::default(),
Expand Down Expand Up @@ -359,10 +364,25 @@ where
debug!(target: "engine::root", "All proofs processed, ending calculation");

let start = Instant::now();
let (state_root, trie_updates) =
self.trie.root_with_updates(&self.proof_worker_handle).map_err(|e| {
ParallelStateRootError::Other(format!("could not calculate state root: {e:?}"))
})?;
let (state_root, trie_updates) = match self.trie.root_with_updates() {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This is the only logical change to this PR. If the block had no state updates then the account trie root node won't necessarily be revealed. Previously we would reveal it on-the-fly using a TrieNodeProvider. Now root_with_updates returns a blinded error. In this case we can assume that the SR hasn't changed, and we return the previous block's state root.

Ok(result) => result,
Err(err)
if matches!(
err.kind(),
SparseStateTrieErrorKind::Sparse(SparseTrieErrorKind::Blind)
) =>
{
// A still-blind account trie means this block never changed state, so preserve
// the cached parent root instead of fetching and revealing
// the unchanged root node.
(self.parent_state_root, TrieUpdates::default())
}
Err(err) => {
return Err(ParallelStateRootError::Other(format!(
"could not calculate state root: {err:?}"
)))
}
};

#[cfg(feature = "trie-debug")]
let debug_recorders = self.trie.take_debug_recorders();
Expand Down Expand Up @@ -873,6 +893,11 @@ enum SparseTrieTaskMessage {
mod tests {
use super::*;
use alloy_primitives::{keccak256, Address, B256, U256};
use reth_provider::{
providers::OverlayStateProviderFactory, test_utils::create_test_provider_factory,
};
use reth_trie_db::ChangesetCache;
use reth_trie_parallel::proof_task::ProofTaskCtx;
use reth_trie_sparse::ArenaParallelSparseTrie;

#[test]
Expand Down Expand Up @@ -953,4 +978,43 @@ mod tests {
assert_eq!(decoded.storage_root, storage_root);
assert_eq!(account_rlp_buf, encoded);
}

#[test]
fn run_returns_parent_root_without_revealing_blind_trie_when_no_state_updates() {
let runtime = reth_tasks::Runtime::test();
let provider_factory = create_test_provider_factory();
let overlay_factory =
OverlayStateProviderFactory::new(provider_factory, ChangesetCache::new());
let proof_worker_handle =
ProofWorkerHandle::new(&runtime, ProofTaskCtx::new(overlay_factory), false);

let default_trie = RevealableSparseTrie::blind_from(ConfigurableSparseTrie::Arena(
ArenaParallelSparseTrie::default(),
));
let trie = SparseStateTrie::default()
.with_accounts_trie(default_trie.clone())
.with_default_storage_trie(default_trie)
.with_updates(true);

let parent_state_root = B256::from([0x55; 32]);
let (updates_tx, updates_rx) = crossbeam_channel::unbounded();
let mut task = SparseTrieCacheTask::new_with_trie(
&runtime,
updates_rx,
proof_worker_handle,
MultiProofTaskMetrics::default(),
trie,
parent_state_root,
1,
);

updates_tx.send(StateRootMessage::FinishedStateUpdates).unwrap();
drop(updates_tx);

let outcome = task.run().expect("state root computation should succeed");

assert_eq!(outcome.state_root, parent_state_root);
assert!(outcome.trie_updates.is_empty());
assert!(task.trie.state_trie_ref().is_none(), "blind trie should not be revealed");
}
}
12 changes: 8 additions & 4 deletions crates/trie/parallel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ reth-primitives-traits = { workspace = true, features = ["dashmap", "std"] }
reth-execution-errors.workspace = true
reth-provider.workspace = true
reth-storage-errors.workspace = true
reth-trie-sparse = { workspace = true, features = ["std"] }
reth-tasks = { workspace = true, features = ["rayon"] }
reth-trie.workspace = true

Expand Down Expand Up @@ -46,6 +45,7 @@ reth-metrics = { workspace = true, optional = true }
metrics = { workspace = true, optional = true }

# `trie-debug` feature
reth-trie-sparse = { workspace = true, optional = true, features = ["trie-debug"] }
rand = { workspace = true, optional = true }

[dev-dependencies]
Expand All @@ -63,13 +63,17 @@ tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] }

[features]
default = ["metrics"]
metrics = ["reth-metrics", "dep:metrics", "reth-trie/metrics", "reth-trie-sparse/metrics"]
trie-debug = ["dep:rand", "reth-trie-sparse/trie-debug"]
metrics = ["reth-metrics", "dep:metrics", "reth-trie/metrics"]
trie-debug = [
"dep:rand",
"dep:reth-trie-sparse",
"reth-trie-sparse?/trie-debug",
]
test-utils = [
"reth-primitives-traits/test-utils",
"reth-provider/test-utils",
"reth-trie-db/test-utils",
"reth-trie-sparse/test-utils",
"reth-trie/test-utils",
"reth-tasks/test-utils",
"reth-trie-sparse?/test-utils",
]
Loading
Loading