Skip to content

Commit 0bba027

Browse files
authored
fix: use remote chain id for pre-fork simulation (#567)
* fix: use remote chain id for pre-fork simulation * Fix error message
1 parent e02eb22 commit 0bba027

File tree

7 files changed

+159
-23
lines changed

7 files changed

+159
-23
lines changed

.changeset/fast-kings-search.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nomicfoundation/edr": patch
3+
---
4+
5+
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.

crates/edr_evm/src/blockchain.rs

+8
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ where
124124
/// Retrieves the instances chain ID.
125125
fn chain_id(&self) -> u64;
126126

127+
/// Retrieves the chain ID of the block at the provided number.
128+
/// The chain ID can be different in fork mode pre- and post-fork block
129+
/// number.
130+
fn chain_id_at_block_number(&self, _block_number: u64) -> Result<u64, Self::BlockchainError> {
131+
// Chain id only depends on the block number in fork mode
132+
Ok(self.chain_id())
133+
}
134+
127135
/// Retrieves the last block in the blockchain.
128136
fn last_block(
129137
&self,

crates/edr_evm/src/blockchain/forked.rs

+12
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,18 @@ where
345345
self.chain_id
346346
}
347347

348+
fn chain_id_at_block_number(&self, block_number: u64) -> Result<u64, Self::BlockchainError> {
349+
if block_number > self.last_block_number() {
350+
return Err(BlockchainError::UnknownBlockNumber);
351+
}
352+
353+
if block_number <= self.fork_block_number {
354+
Ok(self.remote_chain_id())
355+
} else {
356+
Ok(self.chain_id())
357+
}
358+
}
359+
348360
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
349361
fn last_block(
350362
&self,

crates/edr_provider/src/data.rs

+88-22
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,20 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
591591
self.blockchain.chain_id()
592592
}
593593

594+
pub fn chain_id_at_block_spec(
595+
&self,
596+
block_spec: &BlockSpec,
597+
) -> Result<u64, ProviderError<LoggerErrorT>> {
598+
let block_number = self.block_number_by_block_spec(block_spec)?;
599+
600+
let chain_id = if let Some(block_number) = block_number {
601+
self.chain_id_at_block_number(block_number, block_spec)?
602+
} else {
603+
self.blockchain.chain_id()
604+
};
605+
606+
Ok(chain_id)
607+
}
594608
pub fn coinbase(&self) -> Address {
595609
self.beneficiary
596610
}
@@ -607,9 +621,8 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
607621
.ok_or_else(|| ProviderError::InvalidTransactionHash(*transaction_hash))?;
608622

609623
let header = block.header();
610-
let block_spec = Some(BlockSpec::Number(header.number));
611624

612-
let cfg_env = self.create_evm_config(block_spec.as_ref())?;
625+
let cfg_env = self.create_evm_config_at_block_spec(&BlockSpec::Number(header.number))?;
613626

614627
let transactions = block.transactions().to_vec();
615628

@@ -659,7 +672,7 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
659672
block_spec: &BlockSpec,
660673
trace_config: DebugTraceConfig,
661674
) -> Result<DebugTraceResultWithTraces, ProviderError<LoggerErrorT>> {
662-
let cfg_env = self.create_evm_config(Some(block_spec))?;
675+
let cfg_env = self.create_evm_config_at_block_spec(block_spec)?;
663676

664677
let tx_env: TxEnv = transaction.into();
665678

@@ -691,7 +704,7 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
691704
transaction: transaction::Signed,
692705
block_spec: &BlockSpec,
693706
) -> Result<EstimateGasResult, ProviderError<LoggerErrorT>> {
694-
let cfg_env = self.create_evm_config(Some(block_spec))?;
707+
let cfg_env = self.create_evm_config_at_block_spec(block_spec)?;
695708
// Minimum gas cost that is required for transaction to be included in
696709
// a block
697710
let minimum_cost = transaction::initial_cost(&transaction, self.spec_id());
@@ -1422,7 +1435,7 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
14221435
block_spec: &BlockSpec,
14231436
state_overrides: &StateOverrides,
14241437
) -> Result<CallResult, ProviderError<LoggerErrorT>> {
1425-
let cfg_env = self.create_evm_config(Some(block_spec))?;
1438+
let cfg_env = self.create_evm_config_at_block_spec(block_spec)?;
14261439
let tx_env = transaction.into();
14271440

14281441
let mut debugger = Debugger::with_mocker(
@@ -1909,26 +1922,32 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
19091922
Ok(transaction_hash)
19101923
}
19111924

1912-
/// Creates a configuration, taking into the hardfork at the provided
1913-
/// `BlockSpec`. If none is provided, assumes the hardfork for newly
1914-
/// mined blocks.
1925+
/// Wrapper over `Blockchain::chain_id_at_block_number` that handles error
1926+
/// conversion.
1927+
fn chain_id_at_block_number(
1928+
&self,
1929+
block_number: u64,
1930+
block_spec: &BlockSpec,
1931+
) -> Result<u64, ProviderError<LoggerErrorT>> {
1932+
self.blockchain
1933+
.chain_id_at_block_number(block_number)
1934+
.map_err(|err| match err {
1935+
BlockchainError::UnknownBlockNumber => ProviderError::InvalidBlockNumberOrHash {
1936+
block_spec: block_spec.clone(),
1937+
latest_block_number: self.blockchain.last_block_number(),
1938+
},
1939+
_ => ProviderError::Blockchain(err),
1940+
})
1941+
}
1942+
1943+
/// Creates an EVM configuration with the provided hardfork and chain id
19151944
fn create_evm_config(
19161945
&self,
1917-
block_spec: Option<&BlockSpec>,
1946+
spec_id: SpecId,
1947+
chain_id: u64,
19181948
) -> Result<CfgEnvWithHandlerCfg, ProviderError<LoggerErrorT>> {
1919-
let block_number = block_spec
1920-
.map(|block_spec| self.block_number_by_block_spec(block_spec))
1921-
.transpose()?
1922-
.flatten();
1923-
1924-
let spec_id = if let Some(block_number) = block_number {
1925-
self.blockchain.spec_at_block_number(block_number)?
1926-
} else {
1927-
self.blockchain.spec_id()
1928-
};
1929-
19301949
let mut cfg_env = CfgEnv::default();
1931-
cfg_env.chain_id = self.blockchain.chain_id();
1950+
cfg_env.chain_id = chain_id;
19321951
cfg_env.limit_contract_code_size = if self.allow_unlimited_contract_size {
19331952
Some(usize::MAX)
19341953
} else {
@@ -1939,6 +1958,29 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
19391958
Ok(CfgEnvWithHandlerCfg::new_with_spec_id(cfg_env, spec_id))
19401959
}
19411960

1961+
/// Creates a configuration, taking into the hardfork and chain id at the
1962+
/// provided `BlockSpec`.
1963+
fn create_evm_config_at_block_spec(
1964+
&self,
1965+
block_spec: &BlockSpec,
1966+
) -> Result<CfgEnvWithHandlerCfg, ProviderError<LoggerErrorT>> {
1967+
let block_number = self.block_number_by_block_spec(block_spec)?;
1968+
1969+
let spec_id = if let Some(block_number) = block_number {
1970+
self.spec_at_block_number(block_number, block_spec)?
1971+
} else {
1972+
self.blockchain.spec_id()
1973+
};
1974+
1975+
let chain_id = if let Some(block_number) = block_number {
1976+
self.chain_id_at_block_number(block_number, block_spec)?
1977+
} else {
1978+
self.blockchain.chain_id()
1979+
};
1980+
1981+
self.create_evm_config(spec_id, chain_id)
1982+
}
1983+
19421984
fn execute_in_block_context<T>(
19431985
&mut self,
19441986
block_spec: Option<&BlockSpec>,
@@ -1995,7 +2037,8 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
19952037
options.beneficiary = Some(options.beneficiary.unwrap_or(self.beneficiary));
19962038
options.gas_limit = Some(options.gas_limit.unwrap_or_else(|| self.block_gas_limit()));
19972039

1998-
let evm_config = self.create_evm_config(None)?;
2040+
let evm_config =
2041+
self.create_evm_config(self.blockchain.spec_id(), self.blockchain.chain_id())?;
19992042

20002043
if options.mix_hash.is_none() && evm_config.handler_cfg.spec_id >= SpecId::MERGE {
20012044
options.mix_hash = Some(self.prev_randao_generator.next_value());
@@ -2228,6 +2271,24 @@ impl<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpoch> ProviderData<LoggerErr
22282271
}
22292272
}
22302273

2274+
/// Wrapper over `Blockchain::spec_at_block_number` that handles error
2275+
/// conversion.
2276+
fn spec_at_block_number(
2277+
&self,
2278+
block_number: u64,
2279+
block_spec: &BlockSpec,
2280+
) -> Result<SpecId, ProviderError<LoggerErrorT>> {
2281+
self.blockchain
2282+
.spec_at_block_number(block_number)
2283+
.map_err(|err| match err {
2284+
BlockchainError::UnknownBlockNumber => ProviderError::InvalidBlockNumberOrHash {
2285+
block_spec: block_spec.clone(),
2286+
latest_block_number: self.blockchain.last_block_number(),
2287+
},
2288+
_ => ProviderError::Blockchain(err),
2289+
})
2290+
}
2291+
22312292
pub fn sign_transaction_request(
22322293
&self,
22332294
transaction_request: TransactionRequestAndSender,
@@ -2979,6 +3040,11 @@ mod tests {
29793040
let chain_id = fixture.provider_data.chain_id();
29803041
assert_eq!(chain_id, fixture.config.chain_id);
29813042

3043+
let chain_id_at_block = fixture
3044+
.provider_data
3045+
.chain_id_at_block_spec(&BlockSpec::Number(1))?;
3046+
assert_eq!(chain_id_at_block, 1);
3047+
29823048
Ok(())
29833049
}
29843050

crates/edr_provider/src/requests/eth/call.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub(crate) fn resolve_call_request_inner<LoggerErrorT: Debug, TimerT: Clone + Ti
103103
..
104104
} = request;
105105

106-
let chain_id = data.chain_id();
106+
let chain_id = data.chain_id_at_block_spec(block_spec)?;
107107
let from = from.unwrap_or_else(|| data.default_caller());
108108
let gas_limit = gas.unwrap_or_else(|| data.block_gas_limit());
109109
let input = input.map_or(Bytes::new(), Bytes::from);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::str::FromStr as _;
2+
3+
use edr_eth::B256;
4+
use edr_provider::{
5+
hardhat_rpc_types::ForkConfig, test_utils::create_test_config_with_fork, time::CurrentTime,
6+
MethodInvocation, NoopLogger, Provider, ProviderRequest,
7+
};
8+
use edr_test_utils::env::get_alchemy_url;
9+
use tokio::runtime;
10+
11+
// https://github.com/NomicFoundation/edr/issues/533
12+
#[tokio::test(flavor = "multi_thread")]
13+
async fn issue_533() -> anyhow::Result<()> {
14+
let logger = Box::new(NoopLogger);
15+
let subscriber = Box::new(|_event| {});
16+
17+
let mut config = create_test_config_with_fork(Some(ForkConfig {
18+
json_rpc_url: get_alchemy_url(),
19+
block_number: Some(20_384_300),
20+
http_headers: None,
21+
}));
22+
23+
// The default chain id set by Hardhat
24+
config.chain_id = 31337;
25+
26+
let provider = Provider::new(
27+
runtime::Handle::current(),
28+
logger,
29+
subscriber,
30+
config,
31+
CurrentTime,
32+
)?;
33+
34+
let transaction_hash =
35+
B256::from_str("0x0537316f37627655b7fe5e50e23f71cd835b377d1cde4226443c94723d036e32")?;
36+
37+
let result = provider.handle_request(ProviderRequest::Single(
38+
MethodInvocation::DebugTraceTransaction(transaction_hash, None),
39+
))?;
40+
41+
assert!(!result.traces.is_empty());
42+
43+
Ok(())
44+
}

crates/edr_provider/tests/issues/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ mod issue_361;
99
mod issue_384;
1010
mod issue_407;
1111
mod issue_503;
12+
mod issue_533;

0 commit comments

Comments
 (0)