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