diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 8fdf43e09..aff08d466 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -230,7 +230,8 @@ impl PreSimulationState { .into_iter() .map(|btx| { let mut tx = TransactionWithMetadata::from_tx_request(btx.transaction); - tx.zk = btx.zk_tx.map(|metadata| ZkTransaction { factory_deps: metadata.factory_deps }); + tx.zk = + btx.zk_tx.map(|metadata| ZkTransaction { factory_deps: metadata.factory_deps }); tx.rpc = btx.rpc.expect("missing broadcastable tx rpc url"); tx }) diff --git a/crates/zksync/core/src/vm/runner.rs b/crates/zksync/core/src/vm/runner.rs index 80b47efd6..fdc701361 100644 --- a/crates/zksync/core/src/vm/runner.rs +++ b/crates/zksync/core/src/vm/runner.rs @@ -70,6 +70,7 @@ where delegate_as: None, block_number: env.block.number, block_timestamp: env.block.timestamp, + block_hashes: get_historical_block_hashes(&mut ecx), block_basefee: min(max_fee_per_gas.to_ru256(), env.block.basefee), is_create, is_static: false, @@ -159,6 +160,7 @@ where block_number: ecx.env.block.number, block_timestamp: ecx.env.block.timestamp, block_basefee: min(max_fee_per_gas.to_ru256(), ecx.env.block.basefee), + block_hashes: get_historical_block_hashes(ecx), is_create: true, is_static: false, }; @@ -215,6 +217,7 @@ where }, block_number: ecx.env.block.number, block_timestamp: ecx.env.block.timestamp, + block_hashes: get_historical_block_hashes(ecx), block_basefee: min(max_fee_per_gas.to_ru256(), ecx.env.block.basefee), is_create: false, is_static: call.is_static, @@ -270,3 +273,22 @@ pub fn encode_create_params( signature.iter().copied().chain(params).collect() } + +/// Get last 256 block hashes mapped to block numbers +fn get_historical_block_hashes(ecx: &mut EvmContext) -> HashMap { + let mut block_hashes = HashMap::default(); + for i in 0..256u32 { + let (block_number, overflow) = ecx.env.block.number.overflowing_sub(rU256::from(i)); + if overflow { + break + } + match ecx.block_hash(block_number) { + Ok(block_hash) => { + block_hashes.insert(block_number, block_hash); + } + Err(_) => break, + } + } + + block_hashes +} diff --git a/crates/zksync/core/src/vm/tracer.rs b/crates/zksync/core/src/vm/tracer.rs index 8dbf12280..89422ce1d 100644 --- a/crates/zksync/core/src/vm/tracer.rs +++ b/crates/zksync/core/src/vm/tracer.rs @@ -109,6 +109,9 @@ pub struct CallContext { pub is_create: bool, /// Whether the current call is a static call. pub is_static: bool, + /// L1 block hashes to return when `BLOCKHASH` opcode is encountered. This ensures consistency + /// when returning environment data in L2. + pub block_hashes: HashMap>, } /// A tracer to allow for foundry-specific functionality. @@ -302,8 +305,13 @@ impl DynTracer> for CheatcodeTracer if current.code_address == SYSTEM_CONTEXT_ADDRESS && calldata.starts_with(&SELECTOR_BLOCK_HASH) { - self.farcall_handler.set_immediate_return(rU256::ZERO.to_be_bytes_vec()); - return + let block_number = U256::from(&calldata[4..36]); + if let Some(block_hash) = + self.call_context.block_hashes.get(&block_number.to_ru256()) + { + self.farcall_handler.set_immediate_return(block_hash.to_vec()); + return; + } } }