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 consensus_context,
42 subblock_fee_recipients: _,
43 },
44 parent,
45 transactions,
46 output,
47 bundle_state,
48 state_provider,
49 state_root,
50 ..
51 } = input;
52
53 let parent = SealedHeader::new_unhashed(parent.clone().into_header().inner);
54
55 let timestamp_millis_part = evm_env.block_env.timestamp_millis_part;
56
57 let block = self.inner.assemble_block(BlockAssemblerInput::<
59 EthBlockExecutorFactory<TempoReceiptBuilder, TempoChainSpec, TempoEvmFactory>,
60 >::new(
61 evm_env,
62 inner,
63 &parent,
64 transactions,
65 output,
66 bundle_state,
67 state_provider,
68 state_root,
69 ))?;
70
71 Ok(block.map_header(|inner| TempoHeader {
72 inner,
73 general_gas_limit,
74 timestamp_millis_part,
75 shared_gas_limit,
76 consensus_context,
77 }))
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use alloy_consensus::{Signed, TxLegacy};
85 use alloy_evm::{EvmEnv, block::BlockExecutionResult, eth::EthBlockExecutionCtx};
86 use alloy_primitives::{Address, B256, Bytes, Signature, TxKind, U256};
87 use reth_chainspec::EthChainSpec;
88 use reth_evm::execute::BlockAssembler;
89 use reth_primitives_traits::SealedHeader;
90 use reth_storage_api::noop::NoopProvider;
91 use revm::{context::BlockEnv, database::BundleState};
92 use std::collections::HashMap;
93 use tempo_chainspec::spec::MODERATO;
94 use tempo_primitives::{
95 TempoHeader, TempoPrimitives, TempoReceipt, TempoTxEnvelope, TempoTxType,
96 };
97 use tempo_revm::TempoBlockEnv;
98
99 fn create_legacy_tx() -> TempoTxEnvelope {
100 let tx = TxLegacy {
101 chain_id: Some(1),
102 nonce: 0,
103 gas_price: 1,
104 gas_limit: 21000,
105 to: TxKind::Call(Address::ZERO),
106 value: U256::ZERO,
107 input: Bytes::new(),
108 };
109 TempoTxEnvelope::Legacy(Signed::new_unhashed(tx, Signature::test_signature()))
110 }
111
112 fn create_test_receipt(gas_used: u64) -> TempoReceipt {
113 TempoReceipt {
114 tx_type: TempoTxType::Legacy,
115 success: true,
116 cumulative_gas_used: gas_used,
117 logs: vec![],
118 }
119 }
120
121 #[test]
122 fn test_assemble_block() {
123 let chainspec = Arc::new(TempoChainSpec::from_genesis(MODERATO.genesis().clone()));
124 let assembler = TempoBlockAssembler::new(chainspec.clone());
125
126 let block_number = 1u64;
127 let timestamp = 1000u64;
128 let timestamp_millis_part = 500u64;
129 let gas_limit = 30_000_000u64;
130 let general_gas_limit = 10_000_000u64;
131 let shared_gas_limit = 10_000_000u64;
132
133 let evm_env = EvmEnv {
134 block_env: TempoBlockEnv {
135 inner: BlockEnv {
136 number: U256::from(block_number),
137 timestamp: U256::from(timestamp),
138 beneficiary: Address::repeat_byte(0x01),
139 basefee: 1,
140 gas_limit,
141 ..Default::default()
142 },
143 timestamp_millis_part,
144 },
145 ..Default::default()
146 };
147
148 let parent_header = TempoHeader {
149 inner: alloy_consensus::Header {
150 number: 0,
151 timestamp: 0,
152 gas_limit,
153 ..Default::default()
154 },
155 general_gas_limit,
156 timestamp_millis_part: 0,
157 shared_gas_limit,
158 ..Default::default()
159 };
160 let parent = SealedHeader::seal_slow(parent_header);
161
162 let execution_ctx = TempoBlockExecutionCtx {
163 inner: EthBlockExecutionCtx {
164 parent_hash: parent.hash(),
165 parent_beacon_block_root: Some(B256::ZERO),
166 ommers: &[],
167 withdrawals: None,
168 extra_data: Bytes::new(),
169 tx_count_hint: None,
170 slot_number: None,
171 },
172 general_gas_limit,
173 shared_gas_limit,
174 validator_set: None,
175 consensus_context: None,
176 subblock_fee_recipients: HashMap::new(),
177 };
178
179 let tx = create_legacy_tx();
180 let transactions = vec![tx];
181
182 let receipt = create_test_receipt(21000);
183 let output = BlockExecutionResult {
184 receipts: vec![receipt],
185 requests: Default::default(),
186 gas_used: 21000,
187 blob_gas_used: 0,
188 };
189
190 let bundle_state = BundleState::default();
191 let state_provider = NoopProvider::<TempoChainSpec, TempoPrimitives>::new(chainspec);
192 let state_root = B256::ZERO;
193
194 let input = BlockAssemblerInput::<TempoEvmConfig, TempoHeader>::new(
195 evm_env,
196 execution_ctx,
197 &parent,
198 transactions,
199 &output,
200 &bundle_state,
201 &state_provider,
202 state_root,
203 );
204
205 let block = assembler
206 .assemble_block(input)
207 .expect("should assemble block");
208
209 assert_eq!(block.header.inner.number, block_number);
211 assert_eq!(block.header.inner.timestamp, timestamp);
212 assert_eq!(block.header.inner.gas_used, 21000);
213 assert_eq!(block.header.inner.gas_limit, gas_limit);
214 assert_eq!(block.header.inner.parent_hash, parent.hash());
215 assert_eq!(block.header.inner.beneficiary, Address::repeat_byte(0x01));
216 assert_eq!(block.header.inner.state_root, state_root);
217
218 assert_eq!(block.header.general_gas_limit, general_gas_limit);
220 assert_eq!(block.header.shared_gas_limit, shared_gas_limit);
221 assert_eq!(block.header.timestamp_millis_part, timestamp_millis_part);
222
223 assert_eq!(block.body.transactions.len(), 1);
225
226 assert!(block.header.consensus_context.is_none());
228 }
229
230 #[test]
231 fn test_assemble_block_with_consensus_context() {
232 let chainspec = Arc::new(TempoChainSpec::from_genesis(MODERATO.genesis().clone()));
233 let assembler = TempoBlockAssembler::new(chainspec.clone());
234
235 let gas_limit = 30_000_000u64;
236 let general_gas_limit = 10_000_000u64;
237 let shared_gas_limit = 10_000_000u64;
238
239 let ctx = tempo_primitives::TempoConsensusContext {
240 epoch: 1,
241 view: 5,
242 proposer: tempo_primitives::ed25519::PublicKey::from_seed([0xab; 32]),
243 parent_view: 4,
244 };
245
246 let evm_env = EvmEnv {
247 block_env: TempoBlockEnv {
248 inner: BlockEnv {
249 number: U256::from(1),
250 timestamp: U256::from(1000),
251 beneficiary: Address::repeat_byte(0x01),
252 basefee: 1,
253 gas_limit,
254 ..Default::default()
255 },
256 timestamp_millis_part: 0,
257 },
258 ..Default::default()
259 };
260
261 let parent_header = TempoHeader {
262 inner: alloy_consensus::Header {
263 gas_limit,
264 ..Default::default()
265 },
266 general_gas_limit,
267 shared_gas_limit,
268 ..Default::default()
269 };
270 let parent = SealedHeader::seal_slow(parent_header);
271
272 let execution_ctx = TempoBlockExecutionCtx {
273 inner: EthBlockExecutionCtx {
274 parent_hash: parent.hash(),
275 parent_beacon_block_root: Some(B256::ZERO),
276 ommers: &[],
277 withdrawals: None,
278 extra_data: Bytes::new(),
279 tx_count_hint: None,
280 slot_number: None,
281 },
282 general_gas_limit,
283 shared_gas_limit,
284 validator_set: None,
285 consensus_context: Some(ctx),
286 subblock_fee_recipients: HashMap::new(),
287 };
288
289 let transactions = vec![create_legacy_tx()];
290 let output = BlockExecutionResult {
291 receipts: vec![create_test_receipt(21000)],
292 requests: Default::default(),
293 gas_used: 21000,
294 blob_gas_used: 0,
295 };
296
297 let bundle_state = BundleState::default();
298 let state_provider = NoopProvider::<TempoChainSpec, TempoPrimitives>::new(chainspec);
299
300 let input = BlockAssemblerInput::<TempoEvmConfig, TempoHeader>::new(
301 evm_env,
302 execution_ctx,
303 &parent,
304 transactions,
305 &output,
306 &bundle_state,
307 &state_provider,
308 B256::ZERO,
309 );
310
311 let block = assembler
312 .assemble_block(input)
313 .expect("should assemble block");
314
315 assert_eq!(block.header.consensus_context, Some(ctx));
316 }
317}