1use super::{
4 dao_fork, eip6110,
5 receipt_builder::{AlloyReceiptBuilder, ReceiptBuilder, ReceiptBuilderCtx},
6 spec::{EthExecutorSpec, EthSpec},
7 EthEvmFactory,
8};
9use crate::{
10 block::{
11 state_changes::{balance_increment_state, post_block_balance_increments},
12 BlockExecutionError, BlockExecutionResult, BlockExecutor, BlockExecutorFactory,
13 BlockExecutorFor, BlockValidationError, ExecutableTx, OnStateHook,
14 StateChangePostBlockSource, StateChangeSource, SystemCaller,
15 },
16 Database, Evm, EvmFactory, FromRecoveredTx, FromTxWithEncoded,
17};
18use alloc::{borrow::Cow, boxed::Box, vec::Vec};
19use alloy_consensus::{Header, Transaction, TxReceipt};
20use alloy_eips::{eip4895::Withdrawals, eip7685::Requests, Encodable2718};
21use alloy_hardforks::EthereumHardfork;
22use alloy_primitives::{Log, B256};
23use revm::{
24 context::Block, context_interface::result::ResultAndState, database::State, DatabaseCommit,
25 Inspector,
26};
27
28#[derive(Debug, Clone)]
30pub struct EthBlockExecutionCtx<'a> {
31 pub parent_hash: B256,
33 pub parent_beacon_block_root: Option<B256>,
35 pub ommers: &'a [Header],
37 pub withdrawals: Option<Cow<'a, Withdrawals>>,
39}
40
41#[derive(Debug)]
43pub struct EthBlockExecutor<'a, Evm, Spec, R: ReceiptBuilder> {
44 pub spec: Spec,
46
47 pub ctx: EthBlockExecutionCtx<'a>,
49 pub evm: Evm,
51 pub system_caller: SystemCaller<Spec>,
53 pub receipt_builder: R,
55
56 pub receipts: Vec<R::Receipt>,
58 pub gas_used: u64,
60
61 pub blob_gas_used: u64,
64}
65
66impl<'a, Evm, Spec, R> EthBlockExecutor<'a, Evm, Spec, R>
67where
68 Spec: Clone,
69 R: ReceiptBuilder,
70{
71 pub fn new(evm: Evm, ctx: EthBlockExecutionCtx<'a>, spec: Spec, receipt_builder: R) -> Self {
73 Self {
74 evm,
75 ctx,
76 receipts: Vec::new(),
77 gas_used: 0,
78 blob_gas_used: 0,
79 system_caller: SystemCaller::new(spec.clone()),
80 spec,
81 receipt_builder,
82 }
83 }
84}
85
86impl<'db, DB, E, Spec, R> BlockExecutor for EthBlockExecutor<'_, E, Spec, R>
87where
88 DB: Database + 'db,
89 E: Evm<
90 DB = &'db mut State<DB>,
91 Tx: FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>,
92 >,
93 Spec: EthExecutorSpec,
94 R: ReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt<Log = Log>>,
95{
96 type Transaction = R::Transaction;
97 type Receipt = R::Receipt;
98 type Evm = E;
99
100 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
101 let state_clear_flag =
103 self.spec.is_spurious_dragon_active_at_block(self.evm.block().number().saturating_to());
104 self.evm.db_mut().set_state_clear_flag(state_clear_flag);
105
106 self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?;
107 self.system_caller
108 .apply_beacon_root_contract_call(self.ctx.parent_beacon_block_root, &mut self.evm)?;
109
110 Ok(())
111 }
112
113 fn execute_transaction_without_commit(
114 &mut self,
115 tx: impl ExecutableTx<Self>,
116 ) -> Result<ResultAndState<<Self::Evm as Evm>::HaltReason>, BlockExecutionError> {
117 let block_available_gas = self.evm.block().gas_limit() - self.gas_used;
120
121 if tx.tx().gas_limit() > block_available_gas {
122 return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
123 transaction_gas_limit: tx.tx().gas_limit(),
124 block_available_gas,
125 }
126 .into());
127 }
128
129 self.evm.transact(&tx).map_err(|err| {
131 let hash = tx.tx().trie_hash();
132 BlockExecutionError::evm(err, hash)
133 })
134 }
135
136 fn commit_transaction(
137 &mut self,
138 output: ResultAndState<<Self::Evm as Evm>::HaltReason>,
139 tx: impl ExecutableTx<Self>,
140 ) -> Result<u64, BlockExecutionError> {
141 let ResultAndState { result, state } = output;
142
143 self.system_caller.on_state(StateChangeSource::Transaction(self.receipts.len()), &state);
144
145 let gas_used = result.gas_used();
146
147 self.gas_used += gas_used;
149
150 if self.spec.is_cancun_active_at_timestamp(self.evm.block().timestamp().saturating_to()) {
152 let tx_blob_gas_used = tx.tx().blob_gas_used().unwrap_or_default();
153
154 self.blob_gas_used = self.blob_gas_used.saturating_add(tx_blob_gas_used);
155 }
156
157 self.receipts.push(self.receipt_builder.build_receipt(ReceiptBuilderCtx {
159 tx: tx.tx(),
160 evm: &self.evm,
161 result,
162 state: &state,
163 cumulative_gas_used: self.gas_used,
164 }));
165
166 self.evm.db_mut().commit(state);
168
169 Ok(gas_used)
170 }
171
172 fn finish(
173 mut self,
174 ) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> {
175 let requests = if self
176 .spec
177 .is_prague_active_at_timestamp(self.evm.block().timestamp().saturating_to())
178 {
179 let deposit_requests =
181 eip6110::parse_deposits_from_receipts(&self.spec, &self.receipts)?;
182
183 let mut requests = Requests::default();
184
185 if !deposit_requests.is_empty() {
186 requests.push_request_with_type(eip6110::DEPOSIT_REQUEST_TYPE, deposit_requests);
187 }
188
189 requests.extend(self.system_caller.apply_post_execution_changes(&mut self.evm)?);
190 requests
191 } else {
192 Requests::default()
193 };
194
195 let mut balance_increments = post_block_balance_increments(
196 &self.spec,
197 self.evm.block(),
198 self.ctx.ommers,
199 self.ctx.withdrawals.as_deref(),
200 );
201
202 if self
204 .spec
205 .ethereum_fork_activation(EthereumHardfork::Dao)
206 .transitions_at_block(self.evm.block().number().saturating_to())
207 {
208 let drained_balance: u128 = self
210 .evm
211 .db_mut()
212 .drain_balances(dao_fork::DAO_HARDFORK_ACCOUNTS)
213 .map_err(|_| BlockValidationError::IncrementBalanceFailed)?
214 .into_iter()
215 .sum();
216
217 *balance_increments.entry(dao_fork::DAO_HARDFORK_BENEFICIARY).or_default() +=
219 drained_balance;
220 }
221 self.evm
223 .db_mut()
224 .increment_balances(balance_increments.clone())
225 .map_err(|_| BlockValidationError::IncrementBalanceFailed)?;
226
227 self.system_caller.try_on_state_with(|| {
229 balance_increment_state(&balance_increments, self.evm.db_mut()).map(|state| {
230 (
231 StateChangeSource::PostBlock(StateChangePostBlockSource::BalanceIncrements),
232 Cow::Owned(state),
233 )
234 })
235 })?;
236
237 Ok((
238 self.evm,
239 BlockExecutionResult {
240 receipts: self.receipts,
241 requests,
242 gas_used: self.gas_used,
243 blob_gas_used: self.blob_gas_used,
244 },
245 ))
246 }
247
248 fn set_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
249 self.system_caller.with_state_hook(hook);
250 }
251
252 fn evm_mut(&mut self) -> &mut Self::Evm {
253 &mut self.evm
254 }
255
256 fn evm(&self) -> &Self::Evm {
257 &self.evm
258 }
259}
260
261#[derive(Debug, Clone, Default, Copy)]
263pub struct EthBlockExecutorFactory<
264 R = AlloyReceiptBuilder,
265 Spec = EthSpec,
266 EvmFactory = EthEvmFactory,
267> {
268 receipt_builder: R,
270 spec: Spec,
272 evm_factory: EvmFactory,
274}
275
276impl<R, Spec, EvmFactory> EthBlockExecutorFactory<R, Spec, EvmFactory> {
277 pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self {
280 Self { receipt_builder, spec, evm_factory }
281 }
282
283 pub const fn receipt_builder(&self) -> &R {
285 &self.receipt_builder
286 }
287
288 pub const fn spec(&self) -> &Spec {
290 &self.spec
291 }
292
293 pub const fn evm_factory(&self) -> &EvmFactory {
295 &self.evm_factory
296 }
297}
298
299impl<R, Spec, EvmF> BlockExecutorFactory for EthBlockExecutorFactory<R, Spec, EvmF>
300where
301 R: ReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt<Log = Log>>,
302 Spec: EthExecutorSpec,
303 EvmF: EvmFactory<Tx: FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>>,
304 Self: 'static,
305{
306 type EvmFactory = EvmF;
307 type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
308 type Transaction = R::Transaction;
309 type Receipt = R::Receipt;
310
311 fn evm_factory(&self) -> &Self::EvmFactory {
312 &self.evm_factory
313 }
314
315 fn create_executor<'a, DB, I>(
316 &'a self,
317 evm: EvmF::Evm<&'a mut State<DB>, I>,
318 ctx: Self::ExecutionCtx<'a>,
319 ) -> impl BlockExecutorFor<'a, Self, DB, I>
320 where
321 DB: Database + 'a,
322 I: Inspector<EvmF::Context<&'a mut State<DB>>> + 'a,
323 {
324 EthBlockExecutor::new(evm, ctx, &self.spec, &self.receipt_builder)
325 }
326}