1use alloc::{sync::Arc, vec::Vec};
2use core::marker::PhantomData;
3
4use alloy_consensus::{
5 Block, BlockBody, BlockHeader, EMPTY_OMMER_ROOT_HASH, Header, TxReceipt, proofs,
6};
7use alloy_evm::{
8 block::{BlockExecutionError, BlockExecutionResult, BlockExecutorFactory},
9 eth::EthBlockExecutionCtx,
10};
11use alloy_primitives::{B64, B256, U256};
12use arbos::header::{ArbHeaderInfo, derive_arb_header_info, read_l2_base_fee};
13use reth_evm::execute::{BlockAssembler, BlockAssemblerInput};
14use reth_primitives_traits::{Receipt, SignedTransaction, logs_bloom};
15use revm::context::Block as RevmBlock;
16
17#[derive(Debug, Clone, Default)]
25pub struct ArbBlockAssembler<ChainSpec> {
26 _phantom: PhantomData<ChainSpec>,
27}
28
29impl<ChainSpec> ArbBlockAssembler<ChainSpec> {
30 pub fn new(_chain_spec: Arc<ChainSpec>) -> Self {
31 Self {
32 _phantom: PhantomData,
33 }
34 }
35}
36
37impl<F, ChainSpec> BlockAssembler<F> for ArbBlockAssembler<ChainSpec>
38where
39 F: for<'a> BlockExecutorFactory<
40 ExecutionCtx<'a> = EthBlockExecutionCtx<'a>,
41 Transaction: SignedTransaction,
42 Receipt: Receipt,
43 >,
44 ChainSpec: Send + Sync + Unpin + 'static,
45{
46 type Block = Block<F::Transaction>;
47
48 fn assemble_block(
49 &self,
50 input: BlockAssemblerInput<'_, '_, F>,
51 ) -> Result<Self::Block, BlockExecutionError> {
52 let BlockAssemblerInput {
53 evm_env,
54 execution_ctx: ctx,
55 parent,
56 transactions,
57 output: BlockExecutionResult {
58 receipts, gas_used, ..
59 },
60 bundle_state,
61 state_provider,
62 state_root,
63 ..
64 } = input;
65
66 let l2_block_number = parent.number().saturating_add(1);
69
70 let timestamp = evm_env.block_env.timestamp().saturating_to();
71
72 let transactions_root = proofs::calculate_transaction_root(&transactions);
73 let receipts_root = proofs::calculate_receipt_root(
74 &receipts
75 .iter()
76 .map(|r| r.with_bloom_ref())
77 .collect::<Vec<_>>(),
78 );
79 let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs()));
80
81 let arb_info = derive_header_info_from_state(
84 state_provider,
85 bundle_state,
86 evm_env.block_env.beneficiary(),
87 )?;
88
89 let mix_hash = arb_info
90 .as_ref()
91 .map(|info| info.compute_mix_hash())
92 .unwrap_or_else(|| evm_env.block_env.prevrandao().unwrap_or_default());
93
94 let extra_data = arb_info
95 .as_ref()
96 .map(|info| {
97 let mut data = info.send_root.to_vec();
98 data.resize(32, 0);
99 data.into()
100 })
101 .unwrap_or_else(|| ctx.extra_data.clone());
102
103 let extra_bytes = ctx.extra_data.as_ref();
105 let delayed_messages_read = if extra_bytes.len() >= 40 {
106 let mut buf = [0u8; 8];
107 buf.copy_from_slice(&extra_bytes[32..40]);
108 u64::from_be_bytes(buf)
109 } else {
110 0
111 };
112
113 let header = Header {
114 parent_hash: ctx.parent_hash,
115 ommers_hash: EMPTY_OMMER_ROOT_HASH,
116 beneficiary: evm_env.block_env.beneficiary(),
117 state_root,
118 transactions_root,
119 receipts_root,
120 withdrawals_root: None,
121 logs_bloom,
122 timestamp,
123 mix_hash,
124 nonce: B64::from(delayed_messages_read.to_be_bytes()),
125 base_fee_per_gas: Some(
126 read_base_fee_from_state(state_provider, bundle_state)?
127 .unwrap_or(evm_env.block_env.basefee()),
128 ),
129 number: l2_block_number,
130 gas_limit: evm_env.block_env.gas_limit(),
131 difficulty: U256::from(1),
132 gas_used: *gas_used,
133 extra_data,
134 parent_beacon_block_root: None,
135 blob_gas_used: None,
136 excess_blob_gas: None,
137 requests_hash: None,
138 };
139
140 Ok(Block {
141 header,
142 body: BlockBody {
143 transactions,
144 ommers: Default::default(),
145 withdrawals: None,
146 },
147 })
148 }
149}
150
151fn read_base_fee_from_state(
159 state_provider: &dyn reth_storage_api::StateProvider,
160 _bundle_state: &revm_database::BundleState,
161) -> Result<Option<u64>, BlockExecutionError> {
162 let read_slot =
164 |addr: alloy_primitives::Address, slot: B256| state_provider.storage(addr, slot);
165 read_l2_base_fee(&read_slot).map_err(BlockExecutionError::other)
166}
167
168fn derive_header_info_from_state(
173 state_provider: &dyn reth_storage_api::StateProvider,
174 bundle_state: &revm_database::BundleState,
175 coinbase: alloy_primitives::Address,
176) -> Result<Option<ArbHeaderInfo>, BlockExecutionError> {
177 let read_slot = |addr: alloy_primitives::Address, slot: B256| {
178 if let Some(account) = bundle_state.state.get(&addr) {
180 let slot_u256 = U256::from_be_bytes(slot.0);
181 if let Some(storage_slot) = account.storage.get(&slot_u256) {
182 return Ok(Some(storage_slot.present_value));
183 }
184 }
185 state_provider.storage(addr, slot)
187 };
188
189 derive_arb_header_info(&read_slot, coinbase).map_err(BlockExecutionError::other)
190}