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(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, current_fork_timestamp) = fork_timestamps
108 .iter()
109 .position(|ts| &latest.timestamp() < ts)
110 .and_then(|idx| idx.checked_sub(1))
111 .or_else(|| fork_timestamps.len().checked_sub(1))
112 .and_then(|idx| fork_timestamps.get(idx).map(|ts| (idx, *ts)))
113 .ok_or_else(|| RethError::msg("no active timestamp fork found"))?;
114
115 let current = self.build_fork_config_at(current_fork_timestamp, current_precompiles);
116
117 let mut config = EthConfig { current, next: None, last: None };
118
119 if let Some(next_fork_timestamp) = fork_timestamps.get(current_fork_idx + 1).copied() {
120 let fake_header = {
121 let mut header = latest.clone();
122 header.set_timestamp(next_fork_timestamp);
123 header
124 };
125 let next_precompiles = evm_to_precompiles_map(
126 self.evm_config
127 .evm_for_block(EmptyDB::default(), &fake_header)
128 .map_err(RethError::other)?,
129 );
130
131 config.next = Some(self.build_fork_config_at(next_fork_timestamp, next_precompiles));
132 } else {
133 return Ok(config);
135 }
136
137 let last_fork_timestamp = fork_timestamps.last().copied().unwrap();
138 let fake_header = {
139 let mut header = latest;
140 header.set_timestamp(last_fork_timestamp);
141 header
142 };
143 let last_precompiles = evm_to_precompiles_map(
144 self.evm_config
145 .evm_for_block(EmptyDB::default(), &fake_header)
146 .map_err(RethError::other)?,
147 );
148
149 config.last = Some(self.build_fork_config_at(last_fork_timestamp, last_precompiles));
150
151 Ok(config)
152 }
153}
154
155impl<Provider, Evm> EthConfigApiServer for EthConfigHandler<Provider, Evm>
156where
157 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
158 + BlockReaderIdExt<Header: HeaderMut>
159 + 'static,
160 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Provider::Header>> + 'static,
161{
162 fn config(&self) -> RpcResult<EthConfig> {
163 Ok(self.config().map_err(EthApiError::from)?)
164 }
165}
166
167fn evm_to_precompiles_map(
168 evm: impl Evm<Precompiles = PrecompilesMap>,
169) -> BTreeMap<String, Address> {
170 let precompiles = evm.precompiles();
171 precompiles
172 .addresses()
173 .filter_map(|address| {
174 Some((precompiles.get(address)?.precompile_id().name().to_string(), *address))
175 })
176 .collect()
177}