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