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: OpHardforks> OpBlockAssembler<ChainSpec> {
32    /// Builds a block for `input` without any bounds on header `H`.
33    pub fn assemble_block<
34        F: for<'a> BlockExecutorFactory<
35            ExecutionCtx<'a>: Into<OpBlockExecutionCtx>,
36            Transaction: SignedTransaction,
37            Receipt: Receipt + DepositReceipt,
38        >,
39        H,
40    >(
41        &self,
42        input: BlockAssemblerInput<'_, '_, F, H>,
43    ) -> Result<Block<F::Transaction>, BlockExecutionError> {
44        let BlockAssemblerInput {
45            evm_env,
46            execution_ctx: ctx,
47            transactions,
48            output: BlockExecutionResult { receipts, gas_used, .. },
49            bundle_state,
50            state_root,
51            state_provider,
52            ..
53        } = input;
54        let ctx = ctx.into();
55
56        let timestamp = evm_env.block_env.timestamp.saturating_to();
57
58        let transactions_root = proofs::calculate_transaction_root(&transactions);
59        let receipts_root =
60            calculate_receipt_root_no_memo_optimism(receipts, &self.chain_spec, timestamp);
61        let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs()));
62
63        let mut requests_hash = None;
64
65        let withdrawals_root = if self.chain_spec.is_isthmus_active_at_timestamp(timestamp) {
66            // always empty requests hash post isthmus
67            requests_hash = Some(EMPTY_REQUESTS_HASH);
68
69            // withdrawals root field in block header is used for storage root of L2 predeploy
70            // `l2tol1-message-passer`
71            Some(
72                isthmus::withdrawals_root(bundle_state, state_provider)
73                    .map_err(BlockExecutionError::other)?,
74            )
75        } else if self.chain_spec.is_canyon_active_at_timestamp(timestamp) {
76            Some(EMPTY_WITHDRAWALS)
77        } else {
78            None
79        };
80
81        let (excess_blob_gas, blob_gas_used) =
82            if self.chain_spec.is_ecotone_active_at_timestamp(timestamp) {
83                (Some(0), Some(0))
84            } else {
85                (None, None)
86            };
87
88        let header = Header {
89            parent_hash: ctx.parent_hash,
90            ommers_hash: EMPTY_OMMER_ROOT_HASH,
91            beneficiary: evm_env.block_env.beneficiary,
92            state_root,
93            transactions_root,
94            receipts_root,
95            withdrawals_root,
96            logs_bloom,
97            timestamp,
98            mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
99            nonce: BEACON_NONCE.into(),
100            base_fee_per_gas: Some(evm_env.block_env.basefee),
101            number: evm_env.block_env.number.saturating_to(),
102            gas_limit: evm_env.block_env.gas_limit,
103            difficulty: evm_env.block_env.difficulty,
104            gas_used: *gas_used,
105            extra_data: ctx.extra_data,
106            parent_beacon_block_root: ctx.parent_beacon_block_root,
107            blob_gas_used,
108            excess_blob_gas,
109            requests_hash,
110        };
111
112        Ok(Block::new(
113            header,
114            BlockBody {
115                transactions,
116                ommers: Default::default(),
117                withdrawals: self
118                    .chain_spec
119                    .is_canyon_active_at_timestamp(timestamp)
120                    .then(Default::default),
121            },
122        ))
123    }
124}
125
126impl<ChainSpec> Clone for OpBlockAssembler<ChainSpec> {
127    fn clone(&self) -> Self {
128        Self { chain_spec: self.chain_spec.clone() }
129    }
130}
131
132impl<F, ChainSpec> BlockAssembler<F> for OpBlockAssembler<ChainSpec>
133where
134    ChainSpec: OpHardforks,
135    F: for<'a> BlockExecutorFactory<
136        ExecutionCtx<'a> = OpBlockExecutionCtx,
137        Transaction: SignedTransaction,
138        Receipt: Receipt + DepositReceipt,
139    >,
140{
141    type Block = Block<F::Transaction>;
142
143    fn assemble_block(
144        &self,
145        input: BlockAssemblerInput<'_, '_, F>,
146    ) -> Result<Self::Block, BlockExecutionError> {
147        self.assemble_block(input)
148    }
149}