reth_rpc_eth_api/helpers/
config.rs1use alloy_consensus::Header;
4use alloy_eips::eip7910::{EthConfig, EthForkConfig, SystemContract};
5use alloy_evm::precompiles::Precompile;
6use alloy_primitives::Address;
7use jsonrpsee::{core::RpcResult, proc_macros::rpc};
8use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks, Head};
9use reth_errors::{ProviderError, RethError};
10use reth_evm::{precompiles::PrecompilesMap, ConfigureEvm, Evm};
11use reth_node_api::NodePrimitives;
12use reth_revm::db::EmptyDB;
13use reth_rpc_eth_types::EthApiError;
14use reth_storage_api::BlockReaderIdExt;
15use std::collections::BTreeMap;
16
17#[cfg_attr(not(feature = "client"), rpc(server, namespace = "eth"))]
19#[cfg_attr(feature = "client", rpc(server, client, namespace = "eth"))]
20pub trait EthConfigApi {
21 #[method(name = "config")]
23 fn config(&self) -> RpcResult<EthConfig>;
24}
25
26#[derive(Debug, Clone)]
30pub struct EthConfigHandler<Provider, Evm> {
31 provider: Provider,
32 evm_config: Evm,
33}
34
35impl<Provider, Evm> EthConfigHandler<Provider, Evm>
36where
37 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
38 + BlockReaderIdExt<Header = Header>
39 + 'static,
40 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Header>> + 'static,
41{
42 pub const fn new(provider: Provider, evm_config: Evm) -> Self {
44 Self { provider, evm_config }
45 }
46
47 fn build_fork_config_at(
50 &self,
51 timestamp: u64,
52 precompiles: BTreeMap<String, Address>,
53 ) -> Option<EthForkConfig> {
54 let chain_spec = self.provider.chain_spec();
55
56 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
57
58 if chain_spec.is_cancun_active_at_timestamp(timestamp) {
59 system_contracts.extend(SystemContract::cancun());
60 }
61
62 if chain_spec.is_prague_active_at_timestamp(timestamp) {
63 system_contracts
64 .extend(SystemContract::prague(chain_spec.deposit_contract().map(|c| c.address)));
65 }
66
67 let fork_id = chain_spec
69 .fork_id(&Head { timestamp, number: u64::MAX, ..Default::default() })
70 .hash
71 .0
72 .into();
73
74 Some(EthForkConfig {
75 activation_time: timestamp,
76 blob_schedule: chain_spec.blob_params_at_timestamp(timestamp)?,
77 chain_id: chain_spec.chain().id(),
78 fork_id,
79 precompiles,
80 system_contracts,
81 })
82 }
83
84 fn config(&self) -> Result<EthConfig, RethError> {
85 let chain_spec = self.provider.chain_spec();
86 let latest = self
87 .provider
88 .latest_header()?
89 .ok_or_else(|| ProviderError::BestBlockNotFound)?
90 .into_header();
91
92 let current_precompiles = evm_to_precompiles_map(
93 self.evm_config.evm_for_block(EmptyDB::default(), &latest).map_err(RethError::other)?,
94 );
95
96 let mut fork_timestamps =
97 chain_spec.forks_iter().filter_map(|(_, cond)| cond.as_timestamp()).collect::<Vec<_>>();
98 fork_timestamps.sort_unstable();
99 fork_timestamps.dedup();
100
101 let (current_fork_idx, current_fork_timestamp) = fork_timestamps
102 .iter()
103 .position(|ts| &latest.timestamp < ts)
104 .and_then(|idx| idx.checked_sub(1))
105 .or_else(|| fork_timestamps.len().checked_sub(1))
106 .and_then(|idx| fork_timestamps.get(idx).map(|ts| (idx, *ts)))
107 .ok_or_else(|| RethError::msg("no active timestamp fork found"))?;
108
109 let current = self
110 .build_fork_config_at(current_fork_timestamp, current_precompiles)
111 .ok_or_else(|| RethError::msg("no fork config for current fork"))?;
112
113 let mut config = EthConfig { current, next: None, last: None };
114
115 if let Some(next_fork_timestamp) = fork_timestamps.get(current_fork_idx + 1).copied() {
116 let fake_header = {
117 let mut header = latest.clone();
118 header.timestamp = next_fork_timestamp;
119 header
120 };
121 let next_precompiles = evm_to_precompiles_map(
122 self.evm_config
123 .evm_for_block(EmptyDB::default(), &fake_header)
124 .map_err(RethError::other)?,
125 );
126
127 config.next = self.build_fork_config_at(next_fork_timestamp, next_precompiles);
128 } else {
129 return Ok(config);
131 }
132
133 let last_fork_timestamp = fork_timestamps.last().copied().unwrap();
134 let fake_header = {
135 let mut header = latest;
136 header.timestamp = last_fork_timestamp;
137 header
138 };
139 let last_precompiles = evm_to_precompiles_map(
140 self.evm_config
141 .evm_for_block(EmptyDB::default(), &fake_header)
142 .map_err(RethError::other)?,
143 );
144
145 config.last = self.build_fork_config_at(last_fork_timestamp, last_precompiles);
146
147 Ok(config)
148 }
149}
150
151impl<Provider, Evm> EthConfigApiServer for EthConfigHandler<Provider, Evm>
152where
153 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
154 + BlockReaderIdExt<Header = Header>
155 + 'static,
156 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Header>> + 'static,
157{
158 fn config(&self) -> RpcResult<EthConfig> {
159 Ok(self.config().map_err(EthApiError::from)?)
160 }
161}
162
163fn evm_to_precompiles_map(
164 evm: impl Evm<Precompiles = PrecompilesMap>,
165) -> BTreeMap<String, Address> {
166 let precompiles = evm.precompiles();
167 precompiles
168 .addresses()
169 .filter_map(|address| {
170 Some((precompiles.get(address)?.precompile_id().name().to_string(), *address))
171 })
172 .collect()
173}