reth_optimism_evm/
build.rs

1use alloc::sync::Arc;
2use alloy_consensus::{
3    constants::EMPTY_WITHDRAWALS, proofs, Block, BlockBody, Header, TxReceipt,
4    EMPTY_OMMER_ROOT_HASH,
5};
6use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE};
7use alloy_evm::block::BlockExecutorFactory;
8use alloy_op_evm::OpBlockExecutionCtx;
9use alloy_primitives::logs_bloom;
10use reth_evm::execute::{BlockAssembler, BlockAssemblerInput};
11use reth_execution_errors::BlockExecutionError;
12use reth_execution_types::BlockExecutionResult;
13use reth_optimism_consensus::{calculate_receipt_root_no_memo_optimism, isthmus};
14use reth_optimism_forks::OpHardforks;
15use reth_optimism_primitives::DepositReceipt;
16use reth_primitives_traits::{Receipt, SignedTransaction};
17
18/// Block builder for Optimism.
19#[derive(Debug)]
20pub struct OpBlockAssembler<ChainSpec> {
21    chain_spec: Arc<ChainSpec>,
22}
23
24impl<ChainSpec> OpBlockAssembler<ChainSpec> {
25    /// Creates a new [`OpBlockAssembler`].
26    pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
27        Self { chain_spec }
28    }
29}
30
31impl<ChainSpec> Clone for OpBlockAssembler<ChainSpec> {
32    fn clone(&self) -> Self {
33        Self { chain_spec: self.chain_spec.clone() }
34    }
35}
36
37impl<F, ChainSpec> BlockAssembler<F> for OpBlockAssembler<ChainSpec>
38where
39    ChainSpec: OpHardforks,
40    F: for<'a> BlockExecutorFactory<
41        ExecutionCtx<'a> = OpBlockExecutionCtx,
42        Transaction: SignedTransaction,
43        Receipt: Receipt + DepositReceipt,
44    >,
45{
46    type Block = alloy_consensus::Block<F::Transaction>;
47
48    fn assemble_block(
49        &self,
50        input: BlockAssemblerInput<'_, '_, F>,
51    ) -> Result<Self::Block, BlockExecutionError> {
52        let BlockAssemblerInput {
53            evm_env,
54            execution_ctx: ctx,
55            transactions,
56            output: BlockExecutionResult { receipts, gas_used, .. },
57            bundle_state,
58            state_root,
59            state_provider,
60            ..
61        } = input;
62
63        let timestamp = evm_env.block_env.timestamp;
64
65        let transactions_root = proofs::calculate_transaction_root(&transactions);
66        let receipts_root =
67            calculate_receipt_root_no_memo_optimism(receipts, &self.chain_spec, timestamp);
68        let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs()));
69
70        let mut requests_hash = None;
71
72        let withdrawals_root = if self.chain_spec.is_isthmus_active_at_timestamp(timestamp) {
73            // always empty requests hash post isthmus
74            requests_hash = Some(EMPTY_REQUESTS_HASH);
75
76            // withdrawals root field in block header is used for storage root of L2 predeploy
77            // `l2tol1-message-passer`
78            Some(
79                isthmus::withdrawals_root(bundle_state, state_provider)
80                    .map_err(BlockExecutionError::other)?,
81            )
82        } else if self.chain_spec.is_canyon_active_at_timestamp(timestamp) {
83            Some(EMPTY_WITHDRAWALS)
84        } else {
85            None
86        };
87
88        let (excess_blob_gas, blob_gas_used) =
89            if self.chain_spec.is_ecotone_active_at_timestamp(timestamp) {
90                (Some(0), Some(0))
91            } else {
92                (None, None)
93            };
94
95        let header = Header {
96            parent_hash: ctx.parent_hash,
97            ommers_hash: EMPTY_OMMER_ROOT_HASH,
98            beneficiary: evm_env.block_env.beneficiary,
99            state_root,
100            transactions_root,
101            receipts_root,
102            withdrawals_root,
103            logs_bloom,
104            timestamp,
105            mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
106            nonce: BEACON_NONCE.into(),
107            base_fee_per_gas: Some(evm_env.block_env.basefee),
108            number: evm_env.block_env.number,
109            gas_limit: evm_env.block_env.gas_limit,
110            difficulty: evm_env.block_env.difficulty,
111            gas_used: *gas_used,
112            extra_data: ctx.extra_data,
113            parent_beacon_block_root: ctx.parent_beacon_block_root,
114            blob_gas_used,
115            excess_blob_gas,
116            requests_hash,
117        };
118
119        Ok(Block::new(
120            header,
121            BlockBody {
122                transactions,
123                ommers: Default::default(),
124                withdrawals: self
125                    .chain_spec
126                    .is_canyon_active_at_timestamp(timestamp)
127                    .then(Default::default),
128            },
129        ))
130    }
131}