Skip to main content

tempo_evm/
assemble.rs

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/// Assembler for Tempo blocks.
13#[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        // Delegate block building to the inner assembler
57        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        // Verify block header fields
205        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        // Verify Tempo-specific header fields
214        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        // Verify body
219        assert_eq!(block.body.transactions.len(), 1);
220    }
221}