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#[derive(Debug)]
19pub struct EthBlockAssembler<ChainSpec = reth_chainspec::ChainSpec> {
20 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 pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
33 Self { chain_spec }
34 }
35}
36
37impl<ChainSpec: EthChainSpec + EthereumHardforks> EthBlockAssembler<ChainSpec> {
38 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 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 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 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}