1use crate::{
2 TempoEvmConfig, TempoEvmFactory, block::TempoReceiptBuilder, context::TempoBlockExecutionCtx,
3};
4use alloy_evm::{block::BlockExecutionError, eth::EthBlockExecutorFactory};
5use reth_evm::execute::{BlockAssembler, BlockAssemblerInput};
6use reth_evm_ethereum::EthBlockAssembler;
7use reth_primitives_traits::SealedHeader;
8use std::sync::Arc;
9use tempo_chainspec::TempoChainSpec;
10use tempo_primitives::TempoHeader;
11
12#[derive(Debug, Clone)]
14pub struct TempoBlockAssembler {
15 pub(crate) inner: EthBlockAssembler<TempoChainSpec>,
16}
17
18impl TempoBlockAssembler {
19 pub fn new(chain_spec: Arc<TempoChainSpec>) -> Self {
20 Self {
21 inner: EthBlockAssembler::new(chain_spec),
22 }
23 }
24}
25
26impl BlockAssembler<TempoEvmConfig> for TempoBlockAssembler {
27 type Block = tempo_primitives::Block;
28
29 fn assemble_block(
30 &self,
31 input: BlockAssemblerInput<'_, '_, TempoEvmConfig, TempoHeader>,
32 ) -> Result<Self::Block, BlockExecutionError> {
33 let BlockAssemblerInput {
34 evm_env,
35 execution_ctx:
36 TempoBlockExecutionCtx {
37 inner,
38 general_gas_limit,
39 shared_gas_limit,
40 validator_set: _,
41 subblock_fee_recipients: _,
42 },
43 parent,
44 transactions,
45 output,
46 bundle_state,
47 state_provider,
48 state_root,
49 ..
50 } = input;
51
52 let parent = SealedHeader::new_unhashed(parent.clone().into_header().inner);
53
54 let timestamp_millis_part = evm_env.block_env.timestamp_millis_part;
55
56 let block = self.inner.assemble_block(BlockAssemblerInput::<
58 EthBlockExecutorFactory<TempoReceiptBuilder, TempoChainSpec, TempoEvmFactory>,
59 >::new(
60 evm_env,
61 inner,
62 &parent,
63 transactions,
64 output,
65 bundle_state,
66 state_provider,
67 state_root,
68 ))?;
69
70 Ok(block.map_header(|inner| TempoHeader {
71 inner,
72 general_gas_limit,
73 timestamp_millis_part,
74 shared_gas_limit,
75 }))
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use alloy_consensus::{Signed, TxLegacy};
83 use alloy_evm::{EvmEnv, block::BlockExecutionResult, eth::EthBlockExecutionCtx};
84 use alloy_primitives::{Address, B256, Bytes, Signature, TxKind, U256};
85 use reth_chainspec::EthChainSpec;
86 use reth_evm::execute::BlockAssembler;
87 use reth_primitives_traits::SealedHeader;
88 use reth_storage_api::noop::NoopProvider;
89 use revm::{context::BlockEnv, database::BundleState};
90 use std::collections::HashMap;
91 use tempo_chainspec::spec::ANDANTINO;
92 use tempo_primitives::{
93 TempoHeader, TempoPrimitives, TempoReceipt, TempoTxEnvelope, TempoTxType,
94 };
95 use tempo_revm::TempoBlockEnv;
96
97 fn create_legacy_tx() -> TempoTxEnvelope {
98 let tx = TxLegacy {
99 chain_id: Some(1),
100 nonce: 0,
101 gas_price: 1,
102 gas_limit: 21000,
103 to: TxKind::Call(Address::ZERO),
104 value: U256::ZERO,
105 input: Bytes::new(),
106 };
107 TempoTxEnvelope::Legacy(Signed::new_unhashed(tx, Signature::test_signature()))
108 }
109
110 fn create_test_receipt(gas_used: u64) -> TempoReceipt {
111 TempoReceipt {
112 tx_type: TempoTxType::Legacy,
113 success: true,
114 cumulative_gas_used: gas_used,
115 logs: vec![],
116 }
117 }
118
119 #[test]
120 fn test_assemble_block() {
121 let chainspec = Arc::new(TempoChainSpec::from_genesis(ANDANTINO.genesis().clone()));
122 let assembler = TempoBlockAssembler::new(chainspec.clone());
123
124 let block_number = 1u64;
125 let timestamp = 1000u64;
126 let timestamp_millis_part = 500u64;
127 let gas_limit = 30_000_000u64;
128 let general_gas_limit = 10_000_000u64;
129 let shared_gas_limit = 10_000_000u64;
130
131 let evm_env = EvmEnv {
132 block_env: TempoBlockEnv {
133 inner: BlockEnv {
134 number: U256::from(block_number),
135 timestamp: U256::from(timestamp),
136 beneficiary: Address::repeat_byte(0x01),
137 basefee: 1,
138 gas_limit,
139 ..Default::default()
140 },
141 timestamp_millis_part,
142 },
143 ..Default::default()
144 };
145
146 let parent_header = TempoHeader {
147 inner: alloy_consensus::Header {
148 number: 0,
149 timestamp: 0,
150 gas_limit,
151 ..Default::default()
152 },
153 general_gas_limit,
154 timestamp_millis_part: 0,
155 shared_gas_limit,
156 };
157 let parent = SealedHeader::seal_slow(parent_header);
158
159 let execution_ctx = TempoBlockExecutionCtx {
160 inner: EthBlockExecutionCtx {
161 parent_hash: parent.hash(),
162 parent_beacon_block_root: Some(B256::ZERO),
163 ommers: &[],
164 withdrawals: None,
165 extra_data: Bytes::new(),
166 tx_count_hint: None,
167 },
168 general_gas_limit,
169 shared_gas_limit,
170 validator_set: None,
171 subblock_fee_recipients: HashMap::new(),
172 };
173
174 let tx = create_legacy_tx();
175 let transactions = vec![tx];
176
177 let receipt = create_test_receipt(21000);
178 let output = BlockExecutionResult {
179 receipts: vec![receipt],
180 requests: Default::default(),
181 gas_used: 21000,
182 blob_gas_used: 0,
183 };
184
185 let bundle_state = BundleState::default();
186 let state_provider = NoopProvider::<TempoChainSpec, TempoPrimitives>::new(chainspec);
187 let state_root = B256::ZERO;
188
189 let input = BlockAssemblerInput::<TempoEvmConfig, TempoHeader>::new(
190 evm_env,
191 execution_ctx,
192 &parent,
193 transactions,
194 &output,
195 &bundle_state,
196 &state_provider,
197 state_root,
198 );
199
200 let block = assembler
201 .assemble_block(input)
202 .expect("should assemble block");
203
204 assert_eq!(block.header.inner.number, block_number);
206 assert_eq!(block.header.inner.timestamp, timestamp);
207 assert_eq!(block.header.inner.gas_used, 21000);
208 assert_eq!(block.header.inner.gas_limit, gas_limit);
209 assert_eq!(block.header.inner.parent_hash, parent.hash());
210 assert_eq!(block.header.inner.beneficiary, Address::repeat_byte(0x01));
211 assert_eq!(block.header.inner.state_root, state_root);
212
213 assert_eq!(block.header.general_gas_limit, general_gas_limit);
215 assert_eq!(block.header.shared_gas_limit, shared_gas_limit);
216 assert_eq!(block.header.timestamp_millis_part, timestamp_millis_part);
217
218 assert_eq!(block.body.transactions.len(), 1);
220 }
221}