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, TxResult,
15 },
16 Database, Evm, EvmFactory, FromRecoveredTx, FromTxWithEncoded, RecoveredTx,
17};
18use alloc::{borrow::Cow, boxed::Box, vec::Vec};
19use alloy_consensus::{Header, Transaction, TransactionEnvelope, TxReceipt};
20use alloy_eips::{eip4895::Withdrawals, eip7685::Requests, Encodable2718};
21use alloy_hardforks::EthereumHardfork;
22use alloy_primitives::{Bytes, Log, B256};
23use revm::{
24 context::Block,
25 context_interface::result::ResultAndState,
26 database::{DatabaseCommitExt, State},
27 DatabaseCommit, Inspector,
28};
29
30#[derive(Debug, Clone)]
32pub struct EthBlockExecutionCtx<'a> {
33 pub parent_hash: B256,
35 pub parent_beacon_block_root: Option<B256>,
37 pub ommers: &'a [Header],
39 pub withdrawals: Option<Cow<'a, Withdrawals>>,
41 pub extra_data: Bytes,
43 pub tx_count_hint: Option<usize>,
45}
46
47#[derive(Debug)]
49pub struct EthBlockExecutor<'a, Evm, Spec, R: ReceiptBuilder> {
50 pub spec: Spec,
52
53 pub ctx: EthBlockExecutionCtx<'a>,
55 pub evm: Evm,
57 pub system_caller: SystemCaller<Spec>,
59 pub receipt_builder: R,
61
62 pub receipts: Vec<R::Receipt>,
64 pub gas_used: u64,
66
67 pub blob_gas_used: u64,
70}
71
72#[derive(Debug)]
74pub struct EthTxResult<H, T> {
75 pub result: ResultAndState<H>,
77 pub blob_gas_used: u64,
79 pub tx_type: T,
81}
82
83impl<H, T> TxResult for EthTxResult<H, T> {
84 type HaltReason = H;
85
86 fn result(&self) -> &ResultAndState<Self::HaltReason> {
87 &self.result
88 }
89}
90
91impl<'a, Evm, Spec, R> EthBlockExecutor<'a, Evm, Spec, R>
92where
93 Spec: Clone,
94 R: ReceiptBuilder,
95{
96 pub fn new(evm: Evm, ctx: EthBlockExecutionCtx<'a>, spec: Spec, receipt_builder: R) -> Self {
98 let tx_count_hint = ctx.tx_count_hint.unwrap_or_default();
99 Self {
100 evm,
101 ctx,
102 receipts: Vec::with_capacity(tx_count_hint),
103 gas_used: 0,
104 blob_gas_used: 0,
105 system_caller: SystemCaller::new(spec.clone()),
106 spec,
107 receipt_builder,
108 }
109 }
110}
111
112impl<'db, DB, E, Spec, R> BlockExecutor for EthBlockExecutor<'_, E, Spec, R>
113where
114 DB: Database + 'db,
115 E: Evm<
116 DB = &'db mut State<DB>,
117 Tx: FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>,
118 >,
119 Spec: EthExecutorSpec,
120 R: ReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt<Log = Log>>,
121{
122 type Transaction = R::Transaction;
123 type Receipt = R::Receipt;
124 type Evm = E;
125 type Result = EthTxResult<E::HaltReason, <R::Transaction as TransactionEnvelope>::TxType>;
126
127 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
128 let state_clear_flag =
130 self.spec.is_spurious_dragon_active_at_block(self.evm.block().number().saturating_to());
131 self.evm.db_mut().set_state_clear_flag(state_clear_flag);
132
133 self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?;
134 self.system_caller
135 .apply_beacon_root_contract_call(self.ctx.parent_beacon_block_root, &mut self.evm)?;
136
137 Ok(())
138 }
139
140 fn execute_transaction_without_commit(
141 &mut self,
142 tx: impl ExecutableTx<Self>,
143 ) -> Result<Self::Result, BlockExecutionError> {
144 let (tx_env, tx) = tx.into_parts();
145
146 let block_available_gas = self.evm.block().gas_limit() - self.gas_used;
149
150 if tx.tx().gas_limit() > block_available_gas {
151 return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
152 transaction_gas_limit: tx.tx().gas_limit(),
153 block_available_gas,
154 }
155 .into());
156 }
157
158 let result = self.evm.transact(tx_env).map_err(|err| {
160 let hash = tx.tx().trie_hash();
161 BlockExecutionError::evm(err, hash)
162 })?;
163
164 Ok(EthTxResult {
165 result,
166 blob_gas_used: tx.tx().blob_gas_used().unwrap_or_default(),
167 tx_type: tx.tx().tx_type(),
168 })
169 }
170
171 fn commit_transaction(&mut self, output: Self::Result) -> Result<u64, BlockExecutionError> {
172 let EthTxResult { result: ResultAndState { result, state }, blob_gas_used, tx_type } =
173 output;
174
175 self.system_caller.on_state(StateChangeSource::Transaction(self.receipts.len()), &state);
176
177 let gas_used = result.gas_used();
178
179 self.gas_used += gas_used;
181
182 if self.spec.is_cancun_active_at_timestamp(self.evm.block().timestamp().saturating_to()) {
184 self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas_used);
185 }
186
187 self.receipts.push(self.receipt_builder.build_receipt(ReceiptBuilderCtx {
189 tx_type,
190 evm: &self.evm,
191 result,
192 state: &state,
193 cumulative_gas_used: self.gas_used,
194 }));
195
196 self.evm.db_mut().commit(state);
198
199 Ok(gas_used)
200 }
201
202 fn finish(
203 mut self,
204 ) -> Result<(Self::Evm, BlockExecutionResult<R::Receipt>), BlockExecutionError> {
205 let requests = if self
206 .spec
207 .is_prague_active_at_timestamp(self.evm.block().timestamp().saturating_to())
208 {
209 let deposit_requests =
211 eip6110::parse_deposits_from_receipts(&self.spec, &self.receipts)?;
212
213 let mut requests = Requests::default();
214 if !deposit_requests.is_empty() {
215 requests.push_request_with_type(eip6110::DEPOSIT_REQUEST_TYPE, deposit_requests);
216 }
217
218 self.system_caller.append_post_execution_changes(&mut self.evm, &mut requests)?;
219 requests
220 } else {
221 Requests::default()
222 };
223
224 let mut balance_increments = post_block_balance_increments(
225 &self.spec,
226 self.evm.block(),
227 self.ctx.ommers,
228 self.ctx.withdrawals.as_deref(),
229 );
230
231 if self
233 .spec
234 .ethereum_fork_activation(EthereumHardfork::Dao)
235 .transitions_at_block(self.evm.block().number().saturating_to())
236 {
237 let drained_balance: u128 = self
239 .evm
240 .db_mut()
241 .drain_balances(dao_fork::DAO_HARDFORK_ACCOUNTS)
242 .map_err(|_| BlockValidationError::IncrementBalanceFailed)?
243 .into_iter()
244 .sum();
245
246 *balance_increments.entry(dao_fork::DAO_HARDFORK_BENEFICIARY).or_default() +=
248 drained_balance;
249 }
250 self.evm
252 .db_mut()
253 .increment_balances(balance_increments.clone())
254 .map_err(|_| BlockValidationError::IncrementBalanceFailed)?;
255
256 self.system_caller.try_on_state_with(|| {
258 balance_increment_state(&balance_increments, self.evm.db_mut()).map(|state| {
259 (
260 StateChangeSource::PostBlock(StateChangePostBlockSource::BalanceIncrements),
261 Cow::Owned(state),
262 )
263 })
264 })?;
265
266 Ok((
267 self.evm,
268 BlockExecutionResult {
269 receipts: self.receipts,
270 requests,
271 gas_used: self.gas_used,
272 blob_gas_used: self.blob_gas_used,
273 },
274 ))
275 }
276
277 fn set_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
278 self.system_caller.with_state_hook(hook);
279 }
280
281 fn evm_mut(&mut self) -> &mut Self::Evm {
282 &mut self.evm
283 }
284
285 fn evm(&self) -> &Self::Evm {
286 &self.evm
287 }
288
289 fn receipts(&self) -> &[Self::Receipt] {
290 &self.receipts
291 }
292}
293
294#[derive(Debug, Clone, Default, Copy)]
296pub struct EthBlockExecutorFactory<
297 R = AlloyReceiptBuilder,
298 Spec = EthSpec,
299 EvmFactory = EthEvmFactory,
300> {
301 receipt_builder: R,
303 spec: Spec,
305 evm_factory: EvmFactory,
307}
308
309impl<R, Spec, EvmFactory> EthBlockExecutorFactory<R, Spec, EvmFactory> {
310 pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self {
313 Self { receipt_builder, spec, evm_factory }
314 }
315
316 pub const fn receipt_builder(&self) -> &R {
318 &self.receipt_builder
319 }
320
321 pub const fn spec(&self) -> &Spec {
323 &self.spec
324 }
325
326 pub const fn evm_factory(&self) -> &EvmFactory {
328 &self.evm_factory
329 }
330}
331
332impl<R, Spec, EvmF> BlockExecutorFactory for EthBlockExecutorFactory<R, Spec, EvmF>
333where
334 R: ReceiptBuilder<Transaction: Transaction + Encodable2718, Receipt: TxReceipt<Log = Log>>,
335 Spec: EthExecutorSpec,
336 EvmF: EvmFactory<Tx: FromRecoveredTx<R::Transaction> + FromTxWithEncoded<R::Transaction>>,
337 Self: 'static,
338{
339 type EvmFactory = EvmF;
340 type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
341 type Transaction = R::Transaction;
342 type Receipt = R::Receipt;
343
344 fn evm_factory(&self) -> &Self::EvmFactory {
345 &self.evm_factory
346 }
347
348 fn create_executor<'a, DB, I>(
349 &'a self,
350 evm: EvmF::Evm<&'a mut State<DB>, I>,
351 ctx: Self::ExecutionCtx<'a>,
352 ) -> impl BlockExecutorFor<'a, Self, DB, I>
353 where
354 DB: Database + 'a,
355 I: Inspector<EvmF::Context<&'a mut State<DB>>> + 'a,
356 {
357 EthBlockExecutor::new(evm, ctx, &self.spec, &self.receipt_builder)
358 }
359}