alloy_evm/block/system_calls/
mod.rs1use crate::{
4 block::{BlockExecutionError, OnStateHook},
5 Evm,
6};
7use alloc::{borrow::Cow, boxed::Box};
8use alloy_consensus::BlockHeader;
9use alloy_eips::{
10 eip7002::WITHDRAWAL_REQUEST_TYPE, eip7251::CONSOLIDATION_REQUEST_TYPE, eip7685::Requests,
11};
12use alloy_hardforks::EthereumHardforks;
13use alloy_primitives::{Bytes, B256};
14use revm::{state::EvmState, DatabaseCommit};
15
16use super::{StateChangePostBlockSource, StateChangePreBlockSource, StateChangeSource};
17
18mod eip2935;
19mod eip4788;
20mod eip7002;
21mod eip7251;
22
23#[derive(derive_more::Debug)]
27pub struct SystemCaller<Spec> {
28 spec: Spec,
29 #[debug(skip)]
31 hook: Option<Box<dyn OnStateHook>>,
32}
33
34impl<Spec> SystemCaller<Spec> {
35 pub const fn new(spec: Spec) -> Self {
38 Self { spec, hook: None }
39 }
40
41 pub fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) -> &mut Self {
43 self.hook = hook;
44 self
45 }
46}
47
48impl<Spec> SystemCaller<Spec>
49where
50 Spec: EthereumHardforks,
51{
52 pub fn apply_pre_execution_changes(
54 &mut self,
55 header: impl BlockHeader,
56 evm: &mut impl Evm<DB: DatabaseCommit>,
57 ) -> Result<(), BlockExecutionError> {
58 self.apply_blockhashes_contract_call(header.parent_hash(), evm)?;
59 self.apply_beacon_root_contract_call(header.parent_beacon_block_root(), evm)?;
60
61 Ok(())
62 }
63
64 pub fn apply_post_execution_changes(
66 &mut self,
67 evm: &mut impl Evm<DB: DatabaseCommit>,
68 ) -> Result<Requests, BlockExecutionError> {
69 let mut requests = Requests::default();
70
71 let withdrawal_requests = self.apply_withdrawal_requests_contract_call(evm)?;
73 if !withdrawal_requests.is_empty() {
74 requests.push_request_with_type(WITHDRAWAL_REQUEST_TYPE, withdrawal_requests);
75 }
76
77 let consolidation_requests = self.apply_consolidation_requests_contract_call(evm)?;
79 if !consolidation_requests.is_empty() {
80 requests.push_request_with_type(CONSOLIDATION_REQUEST_TYPE, consolidation_requests);
81 }
82
83 Ok(requests)
84 }
85
86 pub fn apply_blockhashes_contract_call(
88 &mut self,
89 parent_block_hash: B256,
90 evm: &mut impl Evm<DB: DatabaseCommit>,
91 ) -> Result<(), BlockExecutionError> {
92 let result_and_state =
93 eip2935::transact_blockhashes_contract_call(&self.spec, parent_block_hash, evm)?;
94
95 if let Some(res) = result_and_state {
96 if let Some(hook) = &mut self.hook {
97 hook.on_state(
98 StateChangeSource::PreBlock(StateChangePreBlockSource::BlockHashesContract),
99 &res.state,
100 );
101 }
102 evm.db_mut().commit(res.state);
103 }
104
105 Ok(())
106 }
107
108 pub fn apply_beacon_root_contract_call(
110 &mut self,
111 parent_beacon_block_root: Option<B256>,
112 evm: &mut impl Evm<DB: DatabaseCommit>,
113 ) -> Result<(), BlockExecutionError> {
114 let result_and_state =
115 eip4788::transact_beacon_root_contract_call(&self.spec, parent_beacon_block_root, evm)?;
116
117 if let Some(res) = result_and_state {
118 if let Some(hook) = &mut self.hook {
119 hook.on_state(
120 StateChangeSource::PreBlock(StateChangePreBlockSource::BeaconRootContract),
121 &res.state,
122 );
123 }
124 evm.db_mut().commit(res.state);
125 }
126
127 Ok(())
128 }
129
130 pub fn apply_withdrawal_requests_contract_call(
132 &mut self,
133 evm: &mut impl Evm<DB: DatabaseCommit>,
134 ) -> Result<Bytes, BlockExecutionError> {
135 let result_and_state = eip7002::transact_withdrawal_requests_contract_call(evm)?;
136
137 if let Some(ref mut hook) = &mut self.hook {
138 hook.on_state(
139 StateChangeSource::PostBlock(
140 StateChangePostBlockSource::WithdrawalRequestsContract,
141 ),
142 &result_and_state.state,
143 );
144 }
145 evm.db_mut().commit(result_and_state.state);
146
147 eip7002::post_commit(result_and_state.result)
148 }
149
150 pub fn apply_consolidation_requests_contract_call(
152 &mut self,
153 evm: &mut impl Evm<DB: DatabaseCommit>,
154 ) -> Result<Bytes, BlockExecutionError> {
155 let result_and_state = eip7251::transact_consolidation_requests_contract_call(evm)?;
156
157 if let Some(ref mut hook) = &mut self.hook {
158 hook.on_state(
159 StateChangeSource::PostBlock(
160 StateChangePostBlockSource::ConsolidationRequestsContract,
161 ),
162 &result_and_state.state,
163 );
164 }
165 evm.db_mut().commit(result_and_state.state);
166
167 eip7251::post_commit(result_and_state.result)
168 }
169
170 pub fn on_state(&mut self, source: StateChangeSource, state: &EvmState) {
172 if let Some(hook) = &mut self.hook {
173 hook.on_state(source, state);
174 }
175 }
176
177 pub fn try_on_state_with<'a, F, E>(&mut self, f: F) -> Result<(), E>
179 where
180 F: FnOnce() -> Result<(StateChangeSource, Cow<'a, EvmState>), E>,
181 {
182 self.invoke_hook_with(|hook| {
183 let (source, state) = f()?;
184 hook.on_state(source, &state);
185 Ok(())
186 })
187 .unwrap_or(Ok(()))
188 }
189
190 pub fn on_state_with<'a, F>(&mut self, f: F)
192 where
193 F: FnOnce() -> (StateChangeSource, Cow<'a, EvmState>),
194 {
195 self.invoke_hook_with(|hook| {
196 let (source, state) = f();
197 hook.on_state(source, &state);
198 });
199 }
200
201 pub fn invoke_hook_with<F, R>(&mut self, f: F) -> Option<R>
203 where
204 F: FnOnce(&mut Box<dyn OnStateHook>) -> R,
205 {
206 self.hook.as_mut().map(f)
207 }
208}