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::{logs_bloom as calculate_logs_bloom, Receipt, SignedTransaction};
13use revm::context::Block as _;
14
15#[derive(Debug, Clone)]
17pub struct EthBlockAssembler<ChainSpec = reth_chainspec::ChainSpec> {
18 pub chain_spec: Arc<ChainSpec>,
20}
21
22impl<ChainSpec> EthBlockAssembler<ChainSpec> {
23 pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
25 Self { chain_spec }
26 }
27}
28
29impl<ChainSpec: EthChainSpec + EthereumHardforks> EthBlockAssembler<ChainSpec> {
30 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 = receipts_root.unwrap_or_else(|| {
62 calculate_receipt_root(&receipts.iter().map(|r| r.with_bloom_ref()).collect::<Vec<_>>())
63 });
64 let logs_bloom = logs_bloom
65 .unwrap_or_else(|| calculate_logs_bloom(receipts.iter().flat_map(|r| r.logs())));
66
67 let withdrawals = self
68 .chain_spec
69 .is_shanghai_active_at_timestamp(timestamp)
70 .then(|| Withdrawals::new(ctx.withdrawals.map(|w| w.into_owned()).unwrap_or_default()));
71
72 let withdrawals_root =
73 withdrawals.as_deref().map(|w| proofs::calculate_withdrawals_root(w));
74 let requests_hash = self
75 .chain_spec
76 .is_prague_active_at_timestamp(timestamp)
77 .then(|| requests.requests_hash());
78 let block_number = evm_env.block_env.number().saturating_to();
79 let base_fee_per_gas = self
80 .chain_spec
81 .is_london_active_at_block(block_number)
82 .then(|| evm_env.block_env.basefee());
83
84 let mut excess_blob_gas = None;
85 let mut block_blob_gas_used = None;
86
87 if self.chain_spec.is_cancun_active_at_timestamp(timestamp) {
89 block_blob_gas_used = Some(*blob_gas_used);
90 excess_blob_gas = if self.chain_spec.is_cancun_active_at_timestamp(parent.timestamp) {
91 parent.maybe_next_block_excess_blob_gas(
92 self.chain_spec.blob_params_at_timestamp(timestamp),
93 )
94 } else {
95 Some(
98 alloy_eips::eip7840::BlobParams::cancun()
99 .next_block_excess_blob_gas_osaka(0, 0, 0),
100 )
101 };
102 }
103
104 let header = Header {
105 parent_hash: ctx.parent_hash,
106 ommers_hash: EMPTY_OMMER_ROOT_HASH,
107 beneficiary: evm_env.block_env.beneficiary(),
108 state_root,
109 transactions_root,
110 receipts_root,
111 withdrawals_root,
112 logs_bloom,
113 timestamp,
114 mix_hash: evm_env.block_env.prevrandao().unwrap_or_default(),
115 nonce: BEACON_NONCE.into(),
116 base_fee_per_gas,
117 number: block_number,
118 gas_limit: evm_env.block_env.gas_limit(),
119 difficulty: evm_env.block_env.difficulty(),
120 gas_used: *gas_used,
121 extra_data: ctx.extra_data,
122 parent_beacon_block_root: ctx.parent_beacon_block_root,
123 blob_gas_used: block_blob_gas_used,
124 excess_blob_gas,
125 requests_hash,
126 block_access_list_hash,
127 slot_number: ctx.slot_number,
128 };
129
130 Ok(Block {
131 header,
132 body: BlockBody { transactions, ommers: Default::default(), withdrawals },
133 })
134 }
135}
136
137impl<F, ChainSpec> BlockAssembler<F> for EthBlockAssembler<ChainSpec>
138where
139 F: for<'a> BlockExecutorFactory<
140 ExecutionCtx<'a> = EthBlockExecutionCtx<'a>,
141 Transaction: SignedTransaction,
142 Receipt: Receipt,
143 >,
144 ChainSpec: EthChainSpec + EthereumHardforks,
145{
146 type Block = Block<F::Transaction>;
147
148 fn assemble_block(
149 &self,
150 input: BlockAssemblerInput<'_, '_, F>,
151 ) -> Result<Self::Block, BlockExecutionError> {
152 self.assemble_block(input, None, None, None)
153 }
154}