Skip to main content

alloy_evm/eth/
block.rs

1//! Ethereum block executor.
2
3use 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/// Context for Ethereum block execution.
31#[derive(Debug, Clone)]
32pub struct EthBlockExecutionCtx<'a> {
33    /// Parent block hash.
34    pub parent_hash: B256,
35    /// Parent beacon block root.
36    pub parent_beacon_block_root: Option<B256>,
37    /// Block ommers
38    pub ommers: &'a [Header],
39    /// Block withdrawals.
40    pub withdrawals: Option<Cow<'a, Withdrawals>>,
41    /// Block extra data.
42    pub extra_data: Bytes,
43    /// Block transactions count hint. Used to preallocate the receipts vector.
44    pub tx_count_hint: Option<usize>,
45}
46
47/// Block executor for Ethereum.
48#[derive(Debug)]
49pub struct EthBlockExecutor<'a, Evm, Spec, R: ReceiptBuilder> {
50    /// Reference to the specification object.
51    pub spec: Spec,
52
53    /// Context for block execution.
54    pub ctx: EthBlockExecutionCtx<'a>,
55    /// Inner EVM.
56    pub evm: Evm,
57    /// Utility to call system smart contracts.
58    pub system_caller: SystemCaller<Spec>,
59    /// Receipt builder.
60    pub receipt_builder: R,
61
62    /// Receipts of executed transactions.
63    pub receipts: Vec<R::Receipt>,
64    /// Total gas used by transactions in this block.
65    pub gas_used: u64,
66
67    /// Blob gas used by the block.
68    /// Before cancun activation, this is always 0.
69    pub blob_gas_used: u64,
70}
71
72/// The result of executing an Ethereum transaction.
73#[derive(Debug)]
74pub struct EthTxResult<H, T> {
75    /// Result of the transaction execution.
76    pub result: ResultAndState<H>,
77    /// Blob gas used by the transaction.
78    pub blob_gas_used: u64,
79    /// Type of the transaction.
80    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    /// Creates a new [`EthBlockExecutor`]
97    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        // Set state clear flag if the block is after the Spurious Dragon hardfork.
129        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        // The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior,
147        // must be no greater than the block's gasLimit.
148        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        // Execute transaction and return the result
159        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        // append gas used
180        self.gas_used += gas_used;
181
182        // only determine cancun fields when active
183        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        // Push transaction changeset and calculate header bloom filter for receipt.
188        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        // Commit the state changes.
197        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            // Collect all EIP-6110 deposits
210            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        // Irregular state change at Ethereum DAO hardfork
232        if self
233            .spec
234            .ethereum_fork_activation(EthereumHardfork::Dao)
235            .transitions_at_block(self.evm.block().number().saturating_to())
236        {
237            // drain balances from hardcoded addresses.
238            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            // return balance to DAO beneficiary.
247            *balance_increments.entry(dao_fork::DAO_HARDFORK_BENEFICIARY).or_default() +=
248                drained_balance;
249        }
250        // increment balances
251        self.evm
252            .db_mut()
253            .increment_balances(balance_increments.clone())
254            .map_err(|_| BlockValidationError::IncrementBalanceFailed)?;
255
256        // call state hook with changes due to balance increments.
257        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/// Ethereum block executor factory.
295#[derive(Debug, Clone, Default, Copy)]
296pub struct EthBlockExecutorFactory<
297    R = AlloyReceiptBuilder,
298    Spec = EthSpec,
299    EvmFactory = EthEvmFactory,
300> {
301    /// Receipt builder.
302    receipt_builder: R,
303    /// Chain specification.
304    spec: Spec,
305    /// EVM factory.
306    evm_factory: EvmFactory,
307}
308
309impl<R, Spec, EvmFactory> EthBlockExecutorFactory<R, Spec, EvmFactory> {
310    /// Creates a new [`EthBlockExecutorFactory`] with the given spec, [`EvmFactory`], and
311    /// [`ReceiptBuilder`].
312    pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self {
313        Self { receipt_builder, spec, evm_factory }
314    }
315
316    /// Exposes the receipt builder.
317    pub const fn receipt_builder(&self) -> &R {
318        &self.receipt_builder
319    }
320
321    /// Exposes the chain specification.
322    pub const fn spec(&self) -> &Spec {
323        &self.spec
324    }
325
326    /// Exposes the EVM factory.
327    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}