mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
perf(engine): check block itself as invalid ancestor to eliminate duplicate exec (#22794)
Signed-off-by: Delweng <delweng@gmail.com> Co-authored-by: YK <chiayongkang@hotmail.com>
This commit is contained in:
@@ -2173,18 +2173,26 @@ where
|
||||
|
||||
/// Finds any invalid ancestor for the given payload.
|
||||
///
|
||||
/// This function walks up the chain of buffered ancestors from the payload's block
|
||||
/// hash and checks if any ancestor is marked as invalid in the tree state.
|
||||
/// This function first checks if the block itself is in the invalid headers cache (to
|
||||
/// avoid re-executing a known-invalid block). Then it walks up the chain of buffered
|
||||
/// ancestors and checks if any ancestor is marked as invalid.
|
||||
///
|
||||
/// The check works by:
|
||||
/// 1. Finding the lowest buffered ancestor for the given block hash
|
||||
/// 2. If the ancestor is the same as the block hash itself, using the parent hash instead
|
||||
/// 3. Checking if this ancestor is in the `invalid_headers` map
|
||||
/// 1. Checking if the block hash itself is in the `invalid_headers` map
|
||||
/// 2. Finding the lowest buffered ancestor for the given block hash
|
||||
/// 3. If the ancestor is the same as the block hash itself, using the parent hash instead
|
||||
/// 4. Checking if this ancestor is in the `invalid_headers` map
|
||||
///
|
||||
/// Returns the invalid ancestor block info if found, or None if no invalid ancestor exists.
|
||||
fn find_invalid_ancestor(&mut self, payload: &T::ExecutionData) -> Option<BlockWithParent> {
|
||||
let parent_hash = payload.parent_hash();
|
||||
let block_hash = payload.block_hash();
|
||||
|
||||
// Check if the block itself is already known to be invalid, avoiding re-execution
|
||||
if let Some(entry) = self.state.invalid_headers.get(&block_hash) {
|
||||
return Some(entry);
|
||||
}
|
||||
|
||||
let mut lowest_buffered_ancestor = self.lowest_buffered_ancestor_or(block_hash);
|
||||
if lowest_buffered_ancestor == block_hash {
|
||||
lowest_buffered_ancestor = parent_hash;
|
||||
|
||||
@@ -1582,6 +1582,39 @@ mod check_invalid_ancestors_tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Test that `find_invalid_ancestor` detects the block itself in the invalid cache
|
||||
#[test]
|
||||
fn test_find_invalid_ancestor_detects_block_itself() {
|
||||
reth_tracing::init_test_tracing();
|
||||
|
||||
let mut test_harness = TestHarness::new(HOLESKY.clone());
|
||||
|
||||
// Read block 1
|
||||
let s1 = include_str!("../../test-data/holesky/1.rlp");
|
||||
let data1 = Bytes::from_str(s1).unwrap();
|
||||
let block1 = Block::decode(&mut data1.as_ref()).unwrap();
|
||||
let sealed1 = block1.seal_slow();
|
||||
let hash1 = sealed1.hash();
|
||||
let parent1 = sealed1.parent_hash();
|
||||
|
||||
// Mark block 1 itself as invalid (simulates a block that failed execution)
|
||||
test_harness
|
||||
.tree
|
||||
.state
|
||||
.invalid_headers
|
||||
.insert(BlockWithParent { block: sealed1.num_hash(), parent: parent1 });
|
||||
|
||||
// Create payload for block 1 (same block, sent again by CL)
|
||||
let payload1 = ExecutionData {
|
||||
payload: ExecutionPayloadV1::from_block_unchecked(hash1, &sealed1.into_block()).into(),
|
||||
sidecar: ExecutionPayloadSidecar::none(),
|
||||
};
|
||||
|
||||
// find_invalid_ancestor should detect the block itself without re-execution
|
||||
let result = test_harness.tree.find_invalid_ancestor(&payload1);
|
||||
assert!(result.is_some(), "Should detect block itself in invalid headers cache");
|
||||
}
|
||||
|
||||
/// Helper function to create a malformed payload that descends from a given parent
|
||||
fn create_malformed_payload_descending_from(parent_hash: B256) -> ExecutionData {
|
||||
// Create a block with invalid hash (mismatch between computed and provided hash)
|
||||
|
||||
Reference in New Issue
Block a user