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