1use crate::ConfigureEvm;
4use alloc::{boxed::Box, sync::Arc};
5use alloy_consensus::BlockHeader;
6use alloy_eips::{
7 eip7002::WITHDRAWAL_REQUEST_TYPE, eip7251::CONSOLIDATION_REQUEST_TYPE, eip7685::Requests,
8};
9use alloy_primitives::Bytes;
10use core::fmt::Display;
11use reth_chainspec::EthereumHardforks;
12use reth_execution_errors::BlockExecutionError;
13use revm::{Database, DatabaseCommit, Evm};
14use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, EvmState, B256};
15
16mod eip2935;
17mod eip4788;
18mod eip7002;
19mod eip7251;
20
21pub trait OnStateHook {
23 fn on_state(&mut self, state: &EvmState);
25}
26
27impl<F> OnStateHook for F
28where
29 F: FnMut(&EvmState),
30{
31 fn on_state(&mut self, state: &EvmState) {
32 self(state)
33 }
34}
35
36#[derive(Default, Debug, Clone)]
38#[non_exhaustive]
39pub struct NoopHook;
40
41impl OnStateHook for NoopHook {
42 fn on_state(&mut self, _state: &EvmState) {}
43}
44
45#[allow(missing_debug_implementations)]
49pub struct SystemCaller<EvmConfig, Chainspec> {
50 evm_config: EvmConfig,
51 chain_spec: Arc<Chainspec>,
52 hook: Option<Box<dyn OnStateHook>>,
54}
55
56impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec> {
57 pub const fn new(evm_config: EvmConfig, chain_spec: Arc<Chainspec>) -> Self {
60 Self { evm_config, chain_spec, hook: None }
61 }
62
63 pub fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) -> &mut Self {
65 self.hook = hook;
66 self
67 }
68
69 pub fn finish(self) {}
71}
72
73fn initialize_evm<'a, DB>(
74 db: &'a mut DB,
75 initialized_cfg: &'a CfgEnvWithHandlerCfg,
76 initialized_block_env: &'a BlockEnv,
77) -> Evm<'a, (), &'a mut DB>
78where
79 DB: Database,
80{
81 Evm::builder()
82 .with_db(db)
83 .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env(
84 initialized_cfg.clone(),
85 initialized_block_env.clone(),
86 Default::default(),
87 ))
88 .build()
89}
90
91impl<EvmConfig, Chainspec> SystemCaller<EvmConfig, Chainspec>
92where
93 EvmConfig: ConfigureEvm,
94 Chainspec: EthereumHardforks,
95{
96 pub fn apply_pre_execution_changes<DB, Ext, Block>(
98 &mut self,
99 block: &Block,
100 evm: &mut Evm<'_, Ext, DB>,
101 ) -> Result<(), BlockExecutionError>
102 where
103 DB: Database + DatabaseCommit,
104 DB::Error: Display,
105 Block: reth_primitives_traits::Block<Header = EvmConfig::Header>,
106 {
107 self.apply_blockhashes_contract_call(
108 block.header().timestamp(),
109 block.header().number(),
110 block.header().parent_hash(),
111 evm,
112 )?;
113 self.apply_beacon_root_contract_call(
114 block.header().timestamp(),
115 block.header().number(),
116 block.header().parent_beacon_block_root(),
117 evm,
118 )?;
119
120 Ok(())
121 }
122
123 pub fn apply_post_execution_changes<DB, Ext>(
125 &mut self,
126 evm: &mut Evm<'_, Ext, DB>,
127 ) -> Result<Requests, BlockExecutionError>
128 where
129 DB: Database + DatabaseCommit,
130 DB::Error: Display,
131 {
132 let mut requests = Requests::default();
133
134 let withdrawal_requests = self.apply_withdrawal_requests_contract_call(evm)?;
136 if !withdrawal_requests.is_empty() {
137 requests.push_request(
138 core::iter::once(WITHDRAWAL_REQUEST_TYPE).chain(withdrawal_requests).collect(),
139 );
140 }
141
142 let consolidation_requests = self.apply_consolidation_requests_contract_call(evm)?;
144 if !consolidation_requests.is_empty() {
145 requests.push_request(
146 core::iter::once(CONSOLIDATION_REQUEST_TYPE)
147 .chain(consolidation_requests)
148 .collect(),
149 );
150 }
151
152 Ok(requests)
153 }
154
155 pub fn pre_block_blockhashes_contract_call<DB>(
157 &mut self,
158 db: &mut DB,
159 initialized_cfg: &CfgEnvWithHandlerCfg,
160 initialized_block_env: &BlockEnv,
161 parent_block_hash: B256,
162 ) -> Result<(), BlockExecutionError>
163 where
164 DB: Database + DatabaseCommit,
165 DB::Error: Display,
166 {
167 let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
168 self.apply_blockhashes_contract_call(
169 initialized_block_env.timestamp.to(),
170 initialized_block_env.number.to(),
171 parent_block_hash,
172 &mut evm,
173 )?;
174
175 Ok(())
176 }
177
178 pub fn apply_blockhashes_contract_call<DB, Ext>(
180 &mut self,
181 timestamp: u64,
182 block_number: u64,
183 parent_block_hash: B256,
184 evm: &mut Evm<'_, Ext, DB>,
185 ) -> Result<(), BlockExecutionError>
186 where
187 DB: Database + DatabaseCommit,
188 DB::Error: Display,
189 {
190 let result_and_state = eip2935::transact_blockhashes_contract_call(
191 &self.evm_config,
192 &self.chain_spec,
193 timestamp,
194 block_number,
195 parent_block_hash,
196 evm,
197 )?;
198
199 if let Some(res) = result_and_state {
200 if let Some(ref mut hook) = self.hook {
201 hook.on_state(&res.state);
202 }
203 evm.context.evm.db.commit(res.state);
204 }
205
206 Ok(())
207 }
208
209 pub fn pre_block_beacon_root_contract_call<DB>(
211 &mut self,
212 db: &mut DB,
213 initialized_cfg: &CfgEnvWithHandlerCfg,
214 initialized_block_env: &BlockEnv,
215 parent_beacon_block_root: Option<B256>,
216 ) -> Result<(), BlockExecutionError>
217 where
218 DB: Database + DatabaseCommit,
219 DB::Error: Display,
220 {
221 let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
222
223 self.apply_beacon_root_contract_call(
224 initialized_block_env.timestamp.to(),
225 initialized_block_env.number.to(),
226 parent_beacon_block_root,
227 &mut evm,
228 )?;
229
230 Ok(())
231 }
232
233 pub fn apply_beacon_root_contract_call<DB, Ext>(
235 &mut self,
236 timestamp: u64,
237 block_number: u64,
238 parent_block_hash: Option<B256>,
239 evm: &mut Evm<'_, Ext, DB>,
240 ) -> Result<(), BlockExecutionError>
241 where
242 DB: Database + DatabaseCommit,
243 DB::Error: Display,
244 {
245 let result_and_state = eip4788::transact_beacon_root_contract_call(
246 &self.evm_config,
247 &self.chain_spec,
248 timestamp,
249 block_number,
250 parent_block_hash,
251 evm,
252 )?;
253
254 if let Some(res) = result_and_state {
255 if let Some(ref mut hook) = self.hook {
256 hook.on_state(&res.state);
257 }
258 evm.context.evm.db.commit(res.state);
259 }
260
261 Ok(())
262 }
263
264 pub fn post_block_withdrawal_requests_contract_call<DB>(
266 &mut self,
267 db: &mut DB,
268 initialized_cfg: &CfgEnvWithHandlerCfg,
269 initialized_block_env: &BlockEnv,
270 ) -> Result<Bytes, BlockExecutionError>
271 where
272 DB: Database + DatabaseCommit,
273 DB::Error: Display,
274 {
275 let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
276
277 let result = self.apply_withdrawal_requests_contract_call(&mut evm)?;
278
279 Ok(result)
280 }
281
282 pub fn apply_withdrawal_requests_contract_call<DB, Ext>(
284 &mut self,
285 evm: &mut Evm<'_, Ext, DB>,
286 ) -> Result<Bytes, BlockExecutionError>
287 where
288 DB: Database + DatabaseCommit,
289 DB::Error: Display,
290 {
291 let result_and_state =
292 eip7002::transact_withdrawal_requests_contract_call(&self.evm_config.clone(), evm)?;
293
294 if let Some(ref mut hook) = self.hook {
295 hook.on_state(&result_and_state.state);
296 }
297 evm.context.evm.db.commit(result_and_state.state);
298
299 eip7002::post_commit(result_and_state.result)
300 }
301
302 pub fn post_block_consolidation_requests_contract_call<DB>(
304 &mut self,
305 db: &mut DB,
306 initialized_cfg: &CfgEnvWithHandlerCfg,
307 initialized_block_env: &BlockEnv,
308 ) -> Result<Bytes, BlockExecutionError>
309 where
310 DB: Database + DatabaseCommit,
311 DB::Error: Display,
312 {
313 let mut evm = initialize_evm(db, initialized_cfg, initialized_block_env);
314
315 let res = self.apply_consolidation_requests_contract_call(&mut evm)?;
316
317 Ok(res)
318 }
319
320 pub fn apply_consolidation_requests_contract_call<DB, Ext>(
322 &mut self,
323 evm: &mut Evm<'_, Ext, DB>,
324 ) -> Result<Bytes, BlockExecutionError>
325 where
326 DB: Database + DatabaseCommit,
327 DB::Error: Display,
328 {
329 let result_and_state =
330 eip7251::transact_consolidation_requests_contract_call(&self.evm_config.clone(), evm)?;
331
332 if let Some(ref mut hook) = self.hook {
333 hook.on_state(&result_and_state.state);
334 }
335 evm.context.evm.db.commit(result_and_state.state);
336
337 eip7251::post_commit(result_and_state.result)
338 }
339
340 pub fn on_state(&mut self, state: &EvmState) {
342 if let Some(ref mut hook) = &mut self.hook {
343 hook.on_state(state);
344 }
345 }
346}