Skip to main content

reth_evm_ethereum/
build.rs

1use alloc::{sync::Arc, vec::Vec};
2use alloy_consensus::{
3    proofs::{self, calculate_receipt_root},
4    Block, BlockBody, BlockHeader, Header, TxReceipt, EMPTY_OMMER_ROOT_HASH,
5};
6use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE};
7use alloy_evm::{block::BlockExecutorFactory, eth::EthBlockExecutionCtx};
8use alloy_primitives::{Bloom, B256};
9use reth_chainspec::{EthChainSpec, EthereumHardforks};
10use reth_evm::execute::{BlockAssembler, BlockAssemblerInput, BlockExecutionError};
11use reth_execution_types::BlockExecutionResult;
12use reth_primitives_traits::{Receipt, SignedTransaction};
13use revm::context::Block as _;
14
15/// Block builder for Ethereum.
16#[derive(Debug, Clone)]
17pub struct EthBlockAssembler<ChainSpec = reth_chainspec::ChainSpec> {
18    /// The chainspec.
19    pub chain_spec: Arc<ChainSpec>,
20}
21
22impl<ChainSpec> EthBlockAssembler<ChainSpec> {
23    /// Creates a new [`EthBlockAssembler`].
24    pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
25        Self { chain_spec }
26    }
27}
28
29impl<ChainSpec: EthChainSpec + EthereumHardforks> EthBlockAssembler<ChainSpec> {
30    /// Assembles a block. Accepts optional precomputed transaction root, receipt root, and logs
31    /// bloom.
32    pub fn assemble_block<F>(
33        &self,
34        input: BlockAssemblerInput<'_, '_, F>,
35        transactions_root: Option<B256>,
36        receipts_root: Option<B256>,
37        logs_bloom: Option<Bloom>,
38    ) -> Result<Block<F::Transaction>, BlockExecutionError>
39    where
40        F: for<'a> BlockExecutorFactory<
41            ExecutionCtx<'a> = EthBlockExecutionCtx<'a>,
42            Transaction: SignedTransaction,
43            Receipt: Receipt,
44        >,
45    {
46        let BlockAssemblerInput {
47            evm_env,
48            execution_ctx: ctx,
49            parent,
50            transactions,
51            output: BlockExecutionResult { receipts, requests, gas_used, blob_gas_used },
52            state_root,
53            block_access_list_hash,
54            ..
55        } = input;
56
57        let timestamp = evm_env.block_env.timestamp().saturating_to();
58
59        let transactions_root =
60            transactions_root.unwrap_or_else(|| proofs::calculate_transaction_root(&transactions));
61        let (receipts_root, logs_bloom) = match (receipts_root, logs_bloom) {
62            (Some(receipts_root), Some(logs_bloom)) => (receipts_root, logs_bloom),
63            (receipts_root, logs_bloom) => {
64                // The block bloom is the OR of the receipt blooms, so the blooms computed for the
65                // receipts root are reused instead of hashing every log a second time.
66                let receipts_with_bloom =
67                    receipts.iter().map(TxReceipt::with_bloom_ref).collect::<Vec<_>>();
68                (
69                    receipts_root.unwrap_or_else(|| calculate_receipt_root(&receipts_with_bloom)),
70                    logs_bloom.unwrap_or_else(|| {
71                        receipts_with_bloom.iter().fold(Bloom::ZERO, |acc, r| acc | r.bloom_ref())
72                    }),
73                )
74            }
75        };
76
77        let withdrawals = self
78            .chain_spec
79            .is_shanghai_active_at_timestamp(timestamp)
80            .then(|| Withdrawals::new(ctx.withdrawals.map(|w| w.into_owned()).unwrap_or_default()));
81
82        let withdrawals_root =
83            withdrawals.as_deref().map(|w| proofs::calculate_withdrawals_root(w));
84        let requests_hash = self
85            .chain_spec
86            .is_prague_active_at_timestamp(timestamp)
87            .then(|| requests.requests_hash());
88        let block_number = evm_env.block_env.number().saturating_to();
89        let base_fee_per_gas = self
90            .chain_spec
91            .is_london_active_at_block(block_number)
92            .then(|| evm_env.block_env.basefee());
93
94        let mut excess_blob_gas = None;
95        let mut block_blob_gas_used = None;
96
97        // only determine cancun fields when active
98        if self.chain_spec.is_cancun_active_at_timestamp(timestamp) {
99            block_blob_gas_used = Some(*blob_gas_used);
100            excess_blob_gas = if self.chain_spec.is_cancun_active_at_timestamp(parent.timestamp) {
101                parent.maybe_next_block_excess_blob_gas(
102                    self.chain_spec.blob_params_at_timestamp(timestamp),
103                )
104            } else {
105                // for the first post-fork block, both parent.blob_gas_used and
106                // parent.excess_blob_gas are evaluated as 0
107                Some(
108                    alloy_eips::eip7840::BlobParams::cancun()
109                        .next_block_excess_blob_gas_osaka(0, 0, 0),
110                )
111            };
112        }
113
114        let header = Header {
115            parent_hash: ctx.parent_hash,
116            ommers_hash: EMPTY_OMMER_ROOT_HASH,
117            beneficiary: evm_env.block_env.beneficiary(),
118            state_root,
119            transactions_root,
120            receipts_root,
121            withdrawals_root,
122            logs_bloom,
123            timestamp,
124            mix_hash: evm_env.block_env.prevrandao().unwrap_or_default(),
125            nonce: BEACON_NONCE.into(),
126            base_fee_per_gas,
127            number: block_number,
128            gas_limit: evm_env.block_env.gas_limit(),
129            difficulty: evm_env.block_env.difficulty(),
130            gas_used: *gas_used,
131            extra_data: ctx.extra_data,
132            parent_beacon_block_root: ctx.parent_beacon_block_root,
133            blob_gas_used: block_blob_gas_used,
134            excess_blob_gas,
135            requests_hash,
136            block_access_list_hash,
137            slot_number: ctx.slot_number,
138        };
139
140        Ok(Block {
141            header,
142            body: BlockBody { transactions, ommers: Default::default(), withdrawals },
143        })
144    }
145}
146
147impl<F, ChainSpec> BlockAssembler<F> for EthBlockAssembler<ChainSpec>
148where
149    F: for<'a> BlockExecutorFactory<
150        ExecutionCtx<'a> = EthBlockExecutionCtx<'a>,
151        Transaction: SignedTransaction,
152        Receipt: Receipt,
153    >,
154    ChainSpec: EthChainSpec + EthereumHardforks,
155{
156    type Block = Block<F::Transaction>;
157
158    fn assemble_block(
159        &self,
160        input: BlockAssemblerInput<'_, '_, F>,
161    ) -> Result<Self::Block, BlockExecutionError> {
162        self.assemble_block(input, None, None, None)
163    }
164}