diff --git a/.changeset/fast-kings-search.md b/.changeset/fast-kings-search.md new file mode 100644 index 000000000..422cdad82 --- /dev/null +++ b/.changeset/fast-kings-search.md @@ -0,0 +1,5 @@ +--- +"@nomicfoundation/edr": patch +--- + +Fixed a bug in fork mode where the locally overridden chain id was used instead of the remote chain id when simulating transactions in pre-fork blocks. diff --git a/crates/edr_evm/src/blockchain.rs b/crates/edr_evm/src/blockchain.rs index c044cc3ca..67221f913 100644 --- a/crates/edr_evm/src/blockchain.rs +++ b/crates/edr_evm/src/blockchain.rs @@ -124,6 +124,14 @@ where /// Retrieves the instances chain ID. fn chain_id(&self) -> u64; + /// Retrieves the chain ID of the block at the provided number. + /// The chain ID can be different in fork mode pre- and post-fork block + /// number. + fn chain_id_at_block_number(&self, _block_number: u64) -> Result { + // Chain id only depends on the block number in fork mode + Ok(self.chain_id()) + } + /// Retrieves the last block in the blockchain. fn last_block( &self, diff --git a/crates/edr_evm/src/blockchain/forked.rs b/crates/edr_evm/src/blockchain/forked.rs index f18610cda..302c617e9 100644 --- a/crates/edr_evm/src/blockchain/forked.rs +++ b/crates/edr_evm/src/blockchain/forked.rs @@ -345,6 +345,18 @@ where self.chain_id } + fn chain_id_at_block_number(&self, block_number: u64) -> Result { + if block_number > self.last_block_number() { + return Err(BlockchainError::UnknownBlockNumber); + } + + if block_number <= self.fork_block_number { + Ok(self.remote_chain_id()) + } else { + Ok(self.chain_id()) + } + } + #[cfg_attr(feature = "tracing", tracing::instrument(skip_all))] fn last_block( &self, diff --git a/crates/edr_provider/src/data.rs b/crates/edr_provider/src/data.rs index 339ba4e05..de47217a9 100644 --- a/crates/edr_provider/src/data.rs +++ b/crates/edr_provider/src/data.rs @@ -591,6 +591,20 @@ impl ProviderData Result> { + let block_number = self.block_number_by_block_spec(block_spec)?; + + let chain_id = if let Some(block_number) = block_number { + self.chain_id_at_block_number(block_number, block_spec)? + } else { + self.blockchain.chain_id() + }; + + Ok(chain_id) + } pub fn coinbase(&self) -> Address { self.beneficiary } @@ -607,9 +621,8 @@ impl ProviderData ProviderData Result> { - let cfg_env = self.create_evm_config(Some(block_spec))?; + let cfg_env = self.create_evm_config_at_block_spec(block_spec)?; let tx_env: TxEnv = transaction.into(); @@ -691,7 +704,7 @@ impl ProviderData Result> { - let cfg_env = self.create_evm_config(Some(block_spec))?; + let cfg_env = self.create_evm_config_at_block_spec(block_spec)?; // Minimum gas cost that is required for transaction to be included in // a block let minimum_cost = transaction::initial_cost(&transaction, self.spec_id()); @@ -1422,7 +1435,7 @@ impl ProviderData Result> { - let cfg_env = self.create_evm_config(Some(block_spec))?; + let cfg_env = self.create_evm_config_at_block_spec(block_spec)?; let tx_env = transaction.into(); let mut debugger = Debugger::with_mocker( @@ -1909,26 +1922,32 @@ impl ProviderData Result> { + self.blockchain + .chain_id_at_block_number(block_number) + .map_err(|err| match err { + BlockchainError::UnknownBlockNumber => ProviderError::InvalidBlockNumberOrHash { + block_spec: block_spec.clone(), + latest_block_number: self.blockchain.last_block_number(), + }, + _ => ProviderError::Blockchain(err), + }) + } + + /// Creates an EVM configuration with the provided hardfork and chain id fn create_evm_config( &self, - block_spec: Option<&BlockSpec>, + spec_id: SpecId, + chain_id: u64, ) -> Result> { - let block_number = block_spec - .map(|block_spec| self.block_number_by_block_spec(block_spec)) - .transpose()? - .flatten(); - - let spec_id = if let Some(block_number) = block_number { - self.blockchain.spec_at_block_number(block_number)? - } else { - self.blockchain.spec_id() - }; - let mut cfg_env = CfgEnv::default(); - cfg_env.chain_id = self.blockchain.chain_id(); + cfg_env.chain_id = chain_id; cfg_env.limit_contract_code_size = if self.allow_unlimited_contract_size { Some(usize::MAX) } else { @@ -1939,6 +1958,29 @@ impl ProviderData Result> { + let block_number = self.block_number_by_block_spec(block_spec)?; + + let spec_id = if let Some(block_number) = block_number { + self.spec_at_block_number(block_number, block_spec)? + } else { + self.blockchain.spec_id() + }; + + let chain_id = if let Some(block_number) = block_number { + self.chain_id_at_block_number(block_number, block_spec)? + } else { + self.blockchain.chain_id() + }; + + self.create_evm_config(spec_id, chain_id) + } + fn execute_in_block_context( &mut self, block_spec: Option<&BlockSpec>, @@ -1995,7 +2037,8 @@ impl ProviderData= SpecId::MERGE { options.mix_hash = Some(self.prev_randao_generator.next_value()); @@ -2228,6 +2271,24 @@ impl ProviderData Result> { + self.blockchain + .spec_at_block_number(block_number) + .map_err(|err| match err { + BlockchainError::UnknownBlockNumber => ProviderError::InvalidBlockNumberOrHash { + block_spec: block_spec.clone(), + latest_block_number: self.blockchain.last_block_number(), + }, + _ => ProviderError::Blockchain(err), + }) + } + pub fn sign_transaction_request( &self, transaction_request: TransactionRequestAndSender, @@ -2979,6 +3040,11 @@ mod tests { let chain_id = fixture.provider_data.chain_id(); assert_eq!(chain_id, fixture.config.chain_id); + let chain_id_at_block = fixture + .provider_data + .chain_id_at_block_spec(&BlockSpec::Number(1))?; + assert_eq!(chain_id_at_block, 1); + Ok(()) } diff --git a/crates/edr_provider/src/requests/eth/call.rs b/crates/edr_provider/src/requests/eth/call.rs index c9aead4bc..07405016e 100644 --- a/crates/edr_provider/src/requests/eth/call.rs +++ b/crates/edr_provider/src/requests/eth/call.rs @@ -103,7 +103,7 @@ pub(crate) fn resolve_call_request_inner anyhow::Result<()> { + let logger = Box::new(NoopLogger); + let subscriber = Box::new(|_event| {}); + + let mut config = create_test_config_with_fork(Some(ForkConfig { + json_rpc_url: get_alchemy_url(), + block_number: Some(20_384_300), + http_headers: None, + })); + + // The default chain id set by Hardhat + config.chain_id = 31337; + + let provider = Provider::new( + runtime::Handle::current(), + logger, + subscriber, + config, + CurrentTime, + )?; + + let transaction_hash = + B256::from_str("0x0537316f37627655b7fe5e50e23f71cd835b377d1cde4226443c94723d036e32")?; + + let result = provider.handle_request(ProviderRequest::Single( + MethodInvocation::DebugTraceTransaction(transaction_hash, None), + ))?; + + assert!(!result.traces.is_empty()); + + Ok(()) +} diff --git a/crates/edr_provider/tests/issues/mod.rs b/crates/edr_provider/tests/issues/mod.rs index 851af44a3..3dcc1a2d2 100644 --- a/crates/edr_provider/tests/issues/mod.rs +++ b/crates/edr_provider/tests/issues/mod.rs @@ -9,3 +9,4 @@ mod issue_361; mod issue_384; mod issue_407; mod issue_503; +mod issue_533;