Skip to content
Open
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
9 changes: 9 additions & 0 deletions src/node/evm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ where
type Executor = BscBlockExecutor<'a, EVM, Spec, R>;

fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
// Parlia pre-execution and the snapshot rebuild resolve the parent by hash via
// the global header reader, which only holds canonical headers. During a
// multi-block eth_simulateV1 the parent is the previous in-memory simulated
// block, so seed it here — by hash only, to leave the canonical block-number
// index intact. Re-seeding an already-canonical parent is a no-op.
crate::node::evm::util::insert_header_to_cache_hash_only(
self.parent.header().clone(),
self.parent.hash(),
);
self.executor.apply_pre_execution_changes()
}

Expand Down
28 changes: 28 additions & 0 deletions src/node/evm/pre_execution_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@ mod tests {
use crate::node::evm::pre_execution::{TURN_LENGTH_CACHE, VALIDATOR_CACHE};
use alloy_primitives::{Address, B256};

/// Multi-block `eth_simulateV1` parent resolution (regression for bnb-chain/reth#194).
/// A simulated block must be resolvable by hash for the next block's parent lookup,
/// and hash-only seeding must not touch the canonical block-number index.
#[test]
fn test_simulated_block_parent_resolvable_by_hash_only() {
use crate::node::evm::util::{insert_header_to_cache_hash_only, HEADER_CACHE_READER};
use alloy_consensus::Header;

// High block number so it can't collide with a real cached/provider header.
let simulated_block_1 =
Header { number: 99_000_000, gas_limit: 140_000_000, ..Default::default() };
let block_1_hash = simulated_block_1.hash_slow();

// Unseeded: invisible to the reader (the None that aborts block 2 today).
assert!(HEADER_CACHE_READER.lock().unwrap().get_header_by_hash(&block_1_hash).is_none());

// Seed by hash, as BscBlockBuilder does before block 2 pre-executes.
insert_header_to_cache_hash_only(simulated_block_1.clone(), block_1_hash);

let resolved = HEADER_CACHE_READER.lock().unwrap().get_header_by_hash(&block_1_hash);
assert_eq!(resolved.map(|h| h.number), Some(simulated_block_1.number));

// Hash-only seeding must leave the canonical block-number index untouched.
let by_number =
HEADER_CACHE_READER.lock().unwrap().get_header_by_number(simulated_block_1.number);
assert!(by_number.is_none());
}

/// Test validator cache basic operations
#[test]
fn test_validator_cache_insert_and_get() {
Expand Down
13 changes: 13 additions & 0 deletions src/node/evm/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ impl HeaderCacheReader {
self.blockhash_to_header.insert(block_hash, header);
tracing::trace!("Insert header to cache, block_number: {:?}, block_hash: {:?}, header: {:?}", block_number, block_hash, header_clone_for_log);
}

/// Insert a header keyed by hash only, leaving the block-number index untouched.
/// Lets non-canonical in-memory blocks (e.g. intermediate `eth_simulateV1` blocks)
/// satisfy by-hash parent lookups without shadowing the real chain by number.
pub fn insert_header_hash_only(&mut self, header: Header, block_hash: BlockHash) {
self.blockhash_to_header.insert(block_hash, header);
}
}

pub static HEADER_CACHE_READER: LazyLock<Mutex<HeaderCacheReader>> = LazyLock::new(|| {
Expand Down Expand Up @@ -110,3 +117,9 @@ pub fn insert_header_to_cache(header: Header) {
pub fn insert_header_to_cache_with_hash(header: Header, block_hash: Option<BlockHash>) {
HEADER_CACHE_READER.lock().unwrap().insert_header_to_cache_with_hash(header, block_hash);
}

/// Insert a header keyed only by its hash (leaves the block-number index alone).
/// See [`HeaderCacheReader::insert_header_hash_only`].
pub fn insert_header_to_cache_hash_only(header: Header, block_hash: BlockHash) {
HEADER_CACHE_READER.lock().unwrap().insert_header_hash_only(header, block_hash);
}