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,
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/// Context for Ethereum block execution.
29#[derive(Debug, Clone)]
30pub struct EthBlockExecutionCtx<'a> {
31    /// Parent block hash.
32    pub parent_hash: B256,
33    /// Parent beacon block root.
34    pub parent_beacon_block_root: Option<B256>,
35    /// Block ommers
36    pub ommers: &'a [Header],
37    /// Block withdrawals.
38    pub withdrawals: Option<Cow<'a, Withdrawals>>,
39}
40
41/// Block executor for Ethereum.
42#[derive(Debug)]
43pub struct EthBlockExecutor<'a, Evm, Spec, R: ReceiptBuilder> {
44    /// Reference to the specification object.
45    pub spec: Spec,
46
47    /// Context for block execution.
48    pub ctx: EthBlockExecutionCtx<'a>,
49    /// Inner EVM.
50    pub evm: Evm,
51    /// Utility to call system smart contracts.
52    pub system_caller: SystemCaller<Spec>,
53    /// Receipt builder.
54    pub receipt_builder: R,
55
56    /// Receipts of executed transactions.
57    pub receipts: Vec<R::Receipt>,
58    /// Total gas used by transactions in this block.
59    pub gas_used: u64,
60
61    /// Blob gas used by the block.
62    /// Before cancun activation, this is always 0.
63    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    /// Creates a new [`EthBlockExecutor`]
72    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        // Set state clear flag if the block is after the Spurious Dragon hardfork.
102        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        // The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior,
118        // must be no greater than the block's gasLimit.
119        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        // Execute transaction and return the result
130        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        // append gas used
148        self.gas_used += gas_used;
149
150        // only determine cancun fields when active
151        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        // Push transaction changeset and calculate header bloom filter for receipt.
158        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        // Commit the state changes.
167        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            // Collect all EIP-6110 deposits
180            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        // Irregular state change at Ethereum DAO hardfork
203        if self
204            .spec
205            .ethereum_fork_activation(EthereumHardfork::Dao)
206            .transitions_at_block(self.evm.block().number().saturating_to())
207        {
208            // drain balances from hardcoded addresses.
209            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            // return balance to DAO beneficiary.
218            *balance_increments.entry(dao_fork::DAO_HARDFORK_BENEFICIARY).or_default() +=
219                drained_balance;
220        }
221        // increment balances
222        self.evm
223            .db_mut()
224            .increment_balances(balance_increments.clone())
225            .map_err(|_| BlockValidationError::IncrementBalanceFailed)?;
226
227        // call state hook with changes due to balance increments.
228        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/// Ethereum block executor factory.
262#[derive(Debug, Clone, Default, Copy)]
263pub struct EthBlockExecutorFactory<
264    R = AlloyReceiptBuilder,
265    Spec = EthSpec,
266    EvmFactory = EthEvmFactory,
267> {
268    /// Receipt builder.
269    receipt_builder: R,
270    /// Chain specification.
271    spec: Spec,
272    /// EVM factory.
273    evm_factory: EvmFactory,
274}
275
276impl<R, Spec, EvmFactory> EthBlockExecutorFactory<R, Spec, EvmFactory> {
277    /// Creates a new [`EthBlockExecutorFactory`] with the given spec, [`EvmFactory`], and
278    /// [`ReceiptBuilder`].
279    pub const fn new(receipt_builder: R, spec: Spec, evm_factory: EvmFactory) -> Self {
280        Self { receipt_builder, spec, evm_factory }
281    }
282
283    /// Exposes the receipt builder.
284    pub const fn receipt_builder(&self) -> &R {
285        &self.receipt_builder
286    }
287
288    /// Exposes the chain specification.
289    pub const fn spec(&self) -> &Spec {
290        &self.spec
291    }
292
293    /// Exposes the EVM factory.
294    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}