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