mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
feat(trie): add trie-debug feature for recording sparse trie mutations (#22234)
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -73,6 +73,7 @@ reth-prune-types = { workspace = true, optional = true }
|
||||
reth-stages = { workspace = true, optional = true }
|
||||
reth-static-file = { workspace = true, optional = true }
|
||||
reth-tracing = { workspace = true, optional = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# reth
|
||||
@@ -143,6 +144,7 @@ test-utils = [
|
||||
"reth-evm-ethereum/test-utils",
|
||||
"reth-tasks/test-utils",
|
||||
]
|
||||
trie-debug = ["reth-trie-sparse/trie-debug", "dep:serde_json"]
|
||||
rocksdb = [
|
||||
"reth-provider/rocksdb",
|
||||
"reth-prune/rocksdb",
|
||||
|
||||
@@ -25,6 +25,8 @@ use reth_trie_parallel::{
|
||||
root::ParallelStateRootError,
|
||||
targets_v2::MultiProofTargetsV2,
|
||||
};
|
||||
#[cfg(feature = "trie-debug")]
|
||||
use reth_trie_sparse::debug_recorder::TrieDebugRecorder;
|
||||
use reth_trie_sparse::{
|
||||
errors::{SparseStateTrieResult, SparseTrieErrorKind, SparseTrieResult},
|
||||
provider::{TrieNodeProvider, TrieNodeProviderFactory},
|
||||
@@ -183,11 +185,19 @@ where
|
||||
ParallelStateRootError::Other(format!("could not calculate state root: {e:?}"))
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "trie-debug")]
|
||||
let debug_recorders = self.trie.take_debug_recorders();
|
||||
|
||||
let end = Instant::now();
|
||||
self.metrics.sparse_trie_final_update_duration_histogram.record(end.duration_since(start));
|
||||
self.metrics.sparse_trie_total_duration_histogram.record(end.duration_since(now));
|
||||
|
||||
Ok(StateRootComputeOutcome { state_root, trie_updates })
|
||||
Ok(StateRootComputeOutcome {
|
||||
state_root,
|
||||
trie_updates,
|
||||
#[cfg(feature = "trie-debug")]
|
||||
debug_recorders,
|
||||
})
|
||||
}
|
||||
|
||||
/// Clears and shrinks the trie, discarding all state.
|
||||
@@ -475,11 +485,19 @@ where
|
||||
ParallelStateRootError::Other(format!("could not calculate state root: {e:?}"))
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "trie-debug")]
|
||||
let debug_recorders = self.trie.take_debug_recorders();
|
||||
|
||||
let end = Instant::now();
|
||||
self.metrics.sparse_trie_final_update_duration_histogram.record(end.duration_since(start));
|
||||
self.metrics.sparse_trie_total_duration_histogram.record(end.duration_since(now));
|
||||
|
||||
Ok(StateRootComputeOutcome { state_root, trie_updates })
|
||||
Ok(StateRootComputeOutcome {
|
||||
state_root,
|
||||
trie_updates,
|
||||
#[cfg(feature = "trie-debug")]
|
||||
debug_recorders,
|
||||
})
|
||||
}
|
||||
|
||||
/// Processes a [`SparseTrieTaskMessage`] from the hashing task.
|
||||
@@ -891,6 +909,10 @@ pub struct StateRootComputeOutcome {
|
||||
pub state_root: B256,
|
||||
/// The trie updates.
|
||||
pub trie_updates: TrieUpdates,
|
||||
/// Debug recorders taken from the sparse tries, keyed by `None` for account trie
|
||||
/// and `Some(address)` for storage tries.
|
||||
#[cfg(feature = "trie-debug")]
|
||||
pub debug_recorders: Vec<(Option<B256>, TrieDebugRecorder)>,
|
||||
}
|
||||
|
||||
/// Updates the sparse trie with the given proofs and state, and returns the elapsed time.
|
||||
|
||||
@@ -15,6 +15,8 @@ use alloy_eip7928::BlockAccessList;
|
||||
use alloy_eips::{eip1898::BlockWithParent, eip4895::Withdrawal, NumHash};
|
||||
use alloy_evm::Evm;
|
||||
use alloy_primitives::B256;
|
||||
#[cfg(feature = "trie-debug")]
|
||||
use reth_trie_sparse::debug_recorder::TrieDebugRecorder;
|
||||
|
||||
use crate::tree::payload_processor::receipt_root_task::{IndexedReceipt, ReceiptRootTaskHandle};
|
||||
use reth_chain_state::{CanonicalInMemoryState, DeferredTrieData, ExecutedBlock, LazyOverlay};
|
||||
@@ -532,6 +534,8 @@ where
|
||||
let root_time = Instant::now();
|
||||
let mut maybe_state_root = None;
|
||||
let mut state_root_task_failed = false;
|
||||
#[cfg(feature = "trie-debug")]
|
||||
let mut trie_debug_recorders = Vec::new();
|
||||
|
||||
match strategy {
|
||||
StateRootStrategy::StateRootTask => {
|
||||
@@ -547,17 +551,34 @@ where
|
||||
);
|
||||
|
||||
match task_result {
|
||||
Ok(StateRootComputeOutcome { state_root, trie_updates }) => {
|
||||
Ok(StateRootComputeOutcome {
|
||||
state_root,
|
||||
trie_updates,
|
||||
#[cfg(feature = "trie-debug")]
|
||||
debug_recorders,
|
||||
}) => {
|
||||
let elapsed = root_time.elapsed();
|
||||
info!(target: "engine::tree::payload_validator", ?state_root, ?elapsed, "State root task finished");
|
||||
|
||||
#[cfg(feature = "trie-debug")]
|
||||
{
|
||||
trie_debug_recorders = debug_recorders;
|
||||
}
|
||||
|
||||
// Compare trie updates with serial computation if configured
|
||||
if self.config.always_compare_trie_updates() {
|
||||
self.compare_trie_updates_with_serial(
|
||||
let _has_diff = self.compare_trie_updates_with_serial(
|
||||
overlay_factory.clone(),
|
||||
&hashed_state,
|
||||
trie_updates.clone(),
|
||||
);
|
||||
#[cfg(feature = "trie-debug")]
|
||||
if _has_diff {
|
||||
Self::write_trie_debug_recorders(
|
||||
block.header().number(),
|
||||
&trie_debug_recorders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// we double check the state root here for good measure
|
||||
@@ -570,6 +591,11 @@ where
|
||||
block_state_root = ?block.header().state_root(),
|
||||
"State root task returned incorrect state root"
|
||||
);
|
||||
#[cfg(feature = "trie-debug")]
|
||||
Self::write_trie_debug_recorders(
|
||||
block.header().number(),
|
||||
&trie_debug_recorders,
|
||||
);
|
||||
state_root_task_failed = true;
|
||||
}
|
||||
}
|
||||
@@ -635,6 +661,9 @@ where
|
||||
|
||||
// ensure state root matches
|
||||
if state_root != block.header().state_root() {
|
||||
#[cfg(feature = "trie-debug")]
|
||||
Self::write_trie_debug_recorders(block.header().number(), &trie_debug_recorders);
|
||||
|
||||
// call post-block hook
|
||||
self.on_invalid_block(
|
||||
&parent_block,
|
||||
@@ -1007,7 +1036,12 @@ where
|
||||
))
|
||||
})?;
|
||||
let (state_root, trie_updates) = result?;
|
||||
return Ok(Ok(StateRootComputeOutcome { state_root, trie_updates }));
|
||||
return Ok(Ok(StateRootComputeOutcome {
|
||||
state_root,
|
||||
trie_updates,
|
||||
#[cfg(feature = "trie-debug")]
|
||||
debug_recorders: Vec::new(),
|
||||
}));
|
||||
}
|
||||
Err(RecvTimeoutError::Timeout) => {}
|
||||
}
|
||||
@@ -1019,7 +1053,12 @@ where
|
||||
"State root timeout race won"
|
||||
);
|
||||
let (state_root, trie_updates) = result?;
|
||||
return Ok(Ok(StateRootComputeOutcome { state_root, trie_updates }));
|
||||
return Ok(Ok(StateRootComputeOutcome {
|
||||
state_root,
|
||||
trie_updates,
|
||||
#[cfg(feature = "trie-debug")]
|
||||
debug_recorders: Vec::new(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1076,7 @@ where
|
||||
overlay_factory: OverlayStateProviderFactory<P>,
|
||||
hashed_state: &HashedPostState,
|
||||
task_trie_updates: TrieUpdates,
|
||||
) {
|
||||
) -> bool {
|
||||
debug!(target: "engine::tree::payload_validator", "Comparing trie updates with serial computation");
|
||||
|
||||
match Self::compute_state_root_serial(overlay_factory.clone(), hashed_state) {
|
||||
@@ -1061,6 +1100,7 @@ where
|
||||
%err,
|
||||
"Error comparing trie updates"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1080,6 +1120,45 @@ where
|
||||
);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Writes trie debug recorders to a JSON file for the given block number.
|
||||
///
|
||||
/// The file is written to the current working directory as
|
||||
/// `trie_debug_block_{block_number}.json`.
|
||||
#[cfg(feature = "trie-debug")]
|
||||
fn write_trie_debug_recorders(
|
||||
block_number: u64,
|
||||
recorders: &[(Option<B256>, TrieDebugRecorder)],
|
||||
) {
|
||||
let path = format!("trie_debug_block_{block_number}.json");
|
||||
match serde_json::to_string_pretty(recorders) {
|
||||
Ok(json) => match std::fs::write(&path, json) {
|
||||
Ok(()) => {
|
||||
warn!(
|
||||
target: "engine::tree::payload_validator",
|
||||
%path,
|
||||
"Wrote trie debug recorders to file"
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
target: "engine::tree::payload_validator",
|
||||
%err,
|
||||
%path,
|
||||
"Failed to write trie debug recorders"
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!(
|
||||
target: "engine::tree::payload_validator",
|
||||
%err,
|
||||
"Failed to serialize trie debug recorders"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the block after execution.
|
||||
|
||||
Reference in New Issue
Block a user