feat: reintroduce --engine.state-root-task-compare-updates (#21717)

This commit is contained in:
Arsenii Kulikov
2026-02-03 03:48:54 +04:00
committed by GitHub
parent f1f3980d29
commit 53f922927a
3 changed files with 67 additions and 3 deletions

View File

@@ -61,7 +61,6 @@ mod persistence_state;
pub mod precompile_cache;
#[cfg(test)]
mod tests;
#[expect(unused)]
mod trie_updates;
use crate::tree::error::AdvancePersistenceError;

View File

@@ -520,6 +520,14 @@ where
info!(target: "engine::tree::payload_validator", ?state_root, ?elapsed, "State root task finished");
// we double check the state root here for good measure
if state_root == block.header().state_root() {
// Compare trie updates with serial computation if configured
if self.config.always_compare_trie_updates() {
self.compare_trie_updates_with_serial(
overlay_factory.clone(),
&hashed_state,
trie_updates.clone(),
);
}
maybe_state_root = Some((state_root, trie_updates, elapsed))
} else {
warn!(
@@ -895,6 +903,62 @@ where
.root_with_updates()?)
}
/// Compares trie updates from the state root task with serial state root computation.
///
/// This is used for debugging and validating the correctness of the parallel state root
/// task implementation. When enabled via `--engine.state-root-task-compare-updates`, this
/// method runs a separate serial state root computation and compares the resulting trie
/// updates.
fn compare_trie_updates_with_serial(
&self,
overlay_factory: OverlayStateProviderFactory<P>,
hashed_state: &HashedPostState,
task_trie_updates: TrieUpdates,
) {
debug!(target: "engine::tree::payload_validator", "Comparing trie updates with serial computation");
match self.compute_state_root_serial(overlay_factory.clone(), hashed_state) {
Ok((serial_root, serial_trie_updates)) => {
debug!(
target: "engine::tree::payload_validator",
?serial_root,
"Serial state root computation finished for comparison"
);
// Get a database provider to use as trie cursor factory
match overlay_factory.database_provider_ro() {
Ok(provider) => {
if let Err(err) = super::trie_updates::compare_trie_updates(
&provider,
task_trie_updates,
serial_trie_updates,
) {
warn!(
target: "engine::tree::payload_validator",
%err,
"Error comparing trie updates"
);
}
}
Err(err) => {
warn!(
target: "engine::tree::payload_validator",
%err,
"Failed to get database provider for trie update comparison"
);
}
}
}
Err(err) => {
warn!(
target: "engine::tree::payload_validator",
%err,
"Failed to compute serial state root for comparison"
);
}
}
}
/// Validates the block after execution.
///
/// This performs:

View File

@@ -98,7 +98,7 @@ impl StorageTrieUpdatesDiff {
/// Compares the trie updates from state root task, regular state root calculation and database,
/// and logs the differences if there's any.
pub(super) fn compare_trie_updates(
pub(crate) fn compare_trie_updates(
trie_cursor_factory: impl TrieCursorFactory,
task: TrieUpdates,
regular: TrieUpdates,
@@ -186,7 +186,8 @@ fn compare_storage_trie_updates<C: TrieCursor>(
task: &mut StorageTrieUpdates,
regular: &mut StorageTrieUpdates,
) -> Result<StorageTrieUpdatesDiff, DatabaseError> {
let database_not_exists = trie_cursor()?.next()?.is_none();
// Check if the storage trie exists by seeking to the first entry
let database_not_exists = trie_cursor()?.seek(Nibbles::default())?.is_none();
let mut diff = StorageTrieUpdatesDiff {
// If the deletion is a no-op, meaning that the entry is not in the
// database, do not add it to the diff.