reth_rpc_eth_api/helpers/
config.rs1use alloy_consensus::{BlockHeader, 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 revm::precompile::PrecompileId;
16use std::{borrow::Borrow, collections::BTreeMap};
17
18#[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 if !chain_spec.is_cancun_active_at_timestamp(latest.timestamp()) {
94 return Err(RethError::msg("cancun has not been activated"))
95 }
96
97 let current_precompiles =
98 evm_to_precompiles_map(self.evm_config.evm_for_block(EmptyDB::default(), &latest));
99
100 let mut fork_timestamps =
101 chain_spec.forks_iter().filter_map(|(_, cond)| cond.as_timestamp()).collect::<Vec<_>>();
102 fork_timestamps.dedup();
103
104 let (current_fork_idx, current_fork_timestamp) = fork_timestamps
105 .iter()
106 .position(|ts| &latest.timestamp < ts)
107 .and_then(|idx| idx.checked_sub(1))
108 .or_else(|| fork_timestamps.len().checked_sub(1))
109 .and_then(|idx| fork_timestamps.get(idx).map(|ts| (idx, *ts)))
110 .ok_or_else(|| RethError::msg("no active timestamp fork found"))?;
111
112 let current = self
113 .build_fork_config_at(current_fork_timestamp, current_precompiles)
114 .ok_or_else(|| RethError::msg("no fork config for current fork"))?;
115
116 let mut config = EthConfig { current, next: None, last: None };
117
118 if let Some(last_fork_idx) = current_fork_idx.checked_sub(1) {
119 if let Some(last_fork_timestamp) = fork_timestamps.get(last_fork_idx).copied() {
120 let fake_header = {
121 let mut header = latest.clone();
122 header.timestamp = last_fork_timestamp;
123 header
124 };
125 let last_precompiles = evm_to_precompiles_map(
126 self.evm_config.evm_for_block(EmptyDB::default(), &fake_header),
127 );
128
129 config.last = self.build_fork_config_at(last_fork_timestamp, last_precompiles);
130 }
131 }
132
133 if let Some(next_fork_timestamp) = fork_timestamps.get(current_fork_idx + 1).copied() {
134 let fake_header = {
135 let mut header = latest;
136 header.timestamp = next_fork_timestamp;
137 header
138 };
139 let next_precompiles = evm_to_precompiles_map(
140 self.evm_config.evm_for_block(EmptyDB::default(), &fake_header),
141 );
142
143 config.next = self.build_fork_config_at(next_fork_timestamp, next_precompiles);
144 }
145
146 Ok(config)
147 }
148}
149
150impl<Provider, Evm> EthConfigApiServer for EthConfigHandler<Provider, Evm>
151where
152 Provider: ChainSpecProvider<ChainSpec: Hardforks + EthereumHardforks>
153 + BlockReaderIdExt<Header = Header>
154 + 'static,
155 Evm: ConfigureEvm<Primitives: NodePrimitives<BlockHeader = Header>> + 'static,
156{
157 fn config(&self) -> RpcResult<EthConfig> {
158 Ok(self.config().map_err(EthApiError::from)?)
159 }
160}
161
162fn evm_to_precompiles_map(
163 evm: impl Evm<Precompiles = PrecompilesMap>,
164) -> BTreeMap<String, Address> {
165 let precompiles = evm.precompiles();
166 precompiles
167 .addresses()
168 .filter_map(|address| {
169 Some((precompile_to_str(precompiles.get(address)?.precompile_id()), *address))
170 })
171 .collect()
172}
173
174fn precompile_to_str(id: &PrecompileId) -> String {
176 let str = match id {
177 PrecompileId::EcRec => "ECREC",
178 PrecompileId::Sha256 => "SHA256",
179 PrecompileId::Ripemd160 => "RIPEMD160",
180 PrecompileId::Identity => "ID",
181 PrecompileId::ModExp => "MODEXP",
182 PrecompileId::Bn254Add => "BN254_ADD",
183 PrecompileId::Bn254Mul => "BN254_MUL",
184 PrecompileId::Bn254Pairing => "BN254_PAIRING",
185 PrecompileId::Blake2F => "BLAKE2F",
186 PrecompileId::KzgPointEvaluation => "KZG_POINT_EVALUATION",
187 PrecompileId::Bls12G1Add => "BLS12_G1ADD",
188 PrecompileId::Bls12G1Msm => "BLS12_G1MSM",
189 PrecompileId::Bls12G2Add => "BLS12_G2ADD",
190 PrecompileId::Bls12G2Msm => "BLS12_G2MSM",
191 PrecompileId::Bls12Pairing => "BLS12_PAIRING_CHECK",
192 PrecompileId::Bls12MapFpToGp1 => "BLS12_MAP_FP_TO_G1",
193 PrecompileId::Bls12MapFp2ToGp2 => "BLS12_MAP_FP2_TO_G2",
194 PrecompileId::P256Verify => "P256_VERIFY",
195 PrecompileId::Custom(custom) => custom.borrow(),
196 };
197 str.to_owned()
198}