tempo_evm/
lib.rs

1//! Tempo EVM implementation.
2
3#![cfg_attr(not(test), warn(unused_crate_dependencies))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6mod assemble;
7use alloy_consensus::{BlockHeader as _, Transaction};
8use alloy_primitives::Address;
9use alloy_rlp::Decodable;
10pub use assemble::TempoBlockAssembler;
11mod block;
12mod context;
13pub use context::{TempoBlockExecutionCtx, TempoNextBlockEnvAttributes};
14mod error;
15pub use error::TempoEvmError;
16pub mod evm;
17use std::{borrow::Cow, sync::Arc};
18
19use alloy_evm::{
20    self, Database, EvmEnv,
21    block::{BlockExecutorFactory, BlockExecutorFor},
22    eth::{EthBlockExecutionCtx, NextEvmEnvAttributes},
23    revm::{Inspector, database::State},
24};
25pub use evm::TempoEvmFactory;
26use reth_chainspec::EthChainSpec;
27use reth_evm::{
28    self, ConfigureEngineEvm, ConfigureEvm, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor,
29};
30use reth_primitives_traits::{SealedBlock, SealedHeader, SignerRecoverable};
31use tempo_payload_types::TempoExecutionData;
32use tempo_primitives::{
33    Block, SubBlockMetadata, TempoHeader, TempoPrimitives, TempoReceipt, TempoTxEnvelope,
34    subblock::PartialValidatorKey,
35};
36
37use crate::{block::TempoBlockExecutor, evm::TempoEvm};
38use reth_evm_ethereum::EthEvmConfig;
39use tempo_chainspec::{TempoChainSpec, hardfork::TempoHardforks};
40use tempo_revm::evm::TempoContext;
41
42pub use tempo_revm::{TempoBlockEnv, TempoHaltReason, TempoStateAccess};
43
44/// Tempo-related EVM configuration.
45#[derive(Debug, Clone)]
46pub struct TempoEvmConfig {
47    /// Inner evm config
48    pub inner: EthEvmConfig<TempoChainSpec, TempoEvmFactory>,
49
50    /// Block assembler
51    pub block_assembler: TempoBlockAssembler,
52}
53
54impl TempoEvmConfig {
55    /// Create a new [`TempoEvmConfig`] with the given chain spec and EVM factory.
56    pub fn new(chain_spec: Arc<TempoChainSpec>, evm_factory: TempoEvmFactory) -> Self {
57        let inner = EthEvmConfig::new_with_evm_factory(chain_spec.clone(), evm_factory);
58        Self {
59            inner,
60            block_assembler: TempoBlockAssembler::new(chain_spec),
61        }
62    }
63
64    /// Create a new [`TempoEvmConfig`] with the given chain spec and default EVM factory.
65    pub fn new_with_default_factory(chain_spec: Arc<TempoChainSpec>) -> Self {
66        Self::new(chain_spec, TempoEvmFactory::default())
67    }
68
69    /// Returns the chain spec
70    pub const fn chain_spec(&self) -> &Arc<TempoChainSpec> {
71        self.inner.chain_spec()
72    }
73
74    /// Returns the inner EVM config
75    pub const fn inner(&self) -> &EthEvmConfig<TempoChainSpec, TempoEvmFactory> {
76        &self.inner
77    }
78}
79
80impl BlockExecutorFactory for TempoEvmConfig {
81    type EvmFactory = TempoEvmFactory;
82    type ExecutionCtx<'a> = TempoBlockExecutionCtx<'a>;
83    type Transaction = TempoTxEnvelope;
84    type Receipt = TempoReceipt;
85
86    fn evm_factory(&self) -> &Self::EvmFactory {
87        self.inner.executor_factory.evm_factory()
88    }
89
90    fn create_executor<'a, DB, I>(
91        &'a self,
92        evm: TempoEvm<&'a mut State<DB>, I>,
93        ctx: Self::ExecutionCtx<'a>,
94    ) -> impl BlockExecutorFor<'a, Self, DB, I>
95    where
96        DB: Database + 'a,
97        I: Inspector<TempoContext<&'a mut State<DB>>> + 'a,
98    {
99        TempoBlockExecutor::new(evm, ctx, self.chain_spec())
100    }
101}
102
103impl ConfigureEvm for TempoEvmConfig {
104    type Primitives = TempoPrimitives;
105    type Error = TempoEvmError;
106    type NextBlockEnvCtx = TempoNextBlockEnvAttributes;
107    type BlockExecutorFactory = Self;
108    type BlockAssembler = TempoBlockAssembler;
109
110    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
111        self
112    }
113
114    fn block_assembler(&self) -> &Self::BlockAssembler {
115        &self.block_assembler
116    }
117
118    fn evm_env(&self, header: &TempoHeader) -> Result<EvmEnvFor<Self>, Self::Error> {
119        let EvmEnv { cfg_env, block_env } = EvmEnv::for_eth_block(
120            header,
121            self.chain_spec(),
122            self.chain_spec().chain().id(),
123            self.chain_spec()
124                .blob_params_at_timestamp(header.timestamp()),
125        );
126
127        let spec = self.chain_spec().tempo_hardfork_at(header.timestamp());
128
129        Ok(EvmEnv {
130            cfg_env: cfg_env.with_spec(spec),
131            block_env: TempoBlockEnv {
132                inner: block_env,
133                timestamp_millis_part: header.timestamp_millis_part,
134            },
135        })
136    }
137
138    fn next_evm_env(
139        &self,
140        parent: &TempoHeader,
141        attributes: &Self::NextBlockEnvCtx,
142    ) -> Result<EvmEnvFor<Self>, Self::Error> {
143        let EvmEnv { cfg_env, block_env } = EvmEnv::for_eth_next_block(
144            parent,
145            NextEvmEnvAttributes {
146                timestamp: attributes.timestamp,
147                suggested_fee_recipient: attributes.suggested_fee_recipient,
148                prev_randao: attributes.prev_randao,
149                gas_limit: attributes.gas_limit,
150            },
151            self.chain_spec()
152                .next_block_base_fee(parent, attributes.timestamp)
153                .unwrap_or_default(),
154            self.chain_spec(),
155            self.chain_spec().chain().id(),
156            self.chain_spec()
157                .blob_params_at_timestamp(attributes.timestamp),
158        );
159
160        let spec = self.chain_spec().tempo_hardfork_at(attributes.timestamp);
161
162        Ok(EvmEnv {
163            cfg_env: cfg_env.with_spec(spec),
164            block_env: TempoBlockEnv {
165                inner: block_env,
166                timestamp_millis_part: attributes.timestamp_millis_part,
167            },
168        })
169    }
170
171    fn context_for_block<'a>(
172        &self,
173        block: &'a SealedBlock<Block>,
174    ) -> Result<TempoBlockExecutionCtx<'a>, Self::Error> {
175        // Decode validator -> fee_recipient mapping from the subblock metadata system transaction.
176        let subblock_fee_recipients = block
177            .body()
178            .transactions
179            .iter()
180            .rev()
181            .filter(|tx| (*tx).to() == Some(Address::ZERO))
182            .find_map(|tx| Vec::<SubBlockMetadata>::decode(&mut tx.input().as_ref()).ok())
183            .ok_or(TempoEvmError::NoSubblockMetadataFound)?
184            .into_iter()
185            .map(|metadata| {
186                (
187                    PartialValidatorKey::from_slice(&metadata.validator[..15]),
188                    metadata.fee_recipient,
189                )
190            })
191            .collect();
192
193        Ok(TempoBlockExecutionCtx {
194            inner: EthBlockExecutionCtx {
195                parent_hash: block.header().parent_hash(),
196                parent_beacon_block_root: block.header().parent_beacon_block_root(),
197                // no ommers in tempo
198                ommers: &[],
199                withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
200            },
201            general_gas_limit: block.header().general_gas_limit,
202            extra_data: block.header().extra_data().clone(),
203            shared_gas_limit: block.header().gas_limit()
204                / tempo_consensus::TEMPO_SHARED_GAS_DIVISOR,
205            // Not available when we only have a block body.
206            validator_set: None,
207            subblock_fee_recipients,
208        })
209    }
210
211    fn context_for_next_block(
212        &self,
213        parent: &SealedHeader<TempoHeader>,
214        attributes: Self::NextBlockEnvCtx,
215    ) -> Result<TempoBlockExecutionCtx<'_>, Self::Error> {
216        Ok(TempoBlockExecutionCtx {
217            inner: EthBlockExecutionCtx {
218                parent_hash: parent.hash(),
219                parent_beacon_block_root: attributes.parent_beacon_block_root,
220                ommers: &[],
221                withdrawals: attributes.inner.withdrawals.map(Cow::Owned),
222            },
223            general_gas_limit: attributes.general_gas_limit,
224            extra_data: attributes.extra_data,
225            shared_gas_limit: attributes.inner.gas_limit
226                / tempo_consensus::TEMPO_SHARED_GAS_DIVISOR,
227            // Fine to not validate during block building.
228            validator_set: None,
229            subblock_fee_recipients: attributes.subblock_fee_recipients,
230        })
231    }
232}
233
234impl ConfigureEngineEvm<TempoExecutionData> for TempoEvmConfig {
235    fn evm_env_for_payload(
236        &self,
237        payload: &TempoExecutionData,
238    ) -> Result<EvmEnvFor<Self>, Self::Error> {
239        self.evm_env(&payload.block)
240    }
241
242    fn context_for_payload<'a>(
243        &self,
244        payload: &'a TempoExecutionData,
245    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
246        let TempoExecutionData {
247            block,
248            validator_set,
249        } = payload;
250        let mut context = self.context_for_block(block)?;
251
252        context.validator_set = validator_set.clone();
253
254        Ok(context)
255    }
256
257    fn tx_iterator_for_payload(
258        &self,
259        payload: &TempoExecutionData,
260    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
261        let transactions = payload.block.body().transactions.clone().into_iter();
262        let convert = |tx: TempoTxEnvelope| tx.try_into_recovered();
263
264        Ok((transactions, convert))
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271    use tempo_chainspec::hardfork::{TempoHardfork, TempoHardforks};
272
273    #[test]
274    fn test_evm_config_can_query_tempo_hardforks() {
275        // Create a test chainspec with Adagio at genesis
276        let chainspec = Arc::new(tempo_chainspec::TempoChainSpec::from_genesis(
277            tempo_chainspec::spec::ANDANTINO.genesis().clone(),
278        ));
279
280        let evm_config = TempoEvmConfig::new_with_default_factory(chainspec);
281
282        // Should be able to query Tempo hardforks through the chainspec
283        assert!(evm_config.chain_spec().is_adagio_active_at_timestamp(0));
284        assert!(evm_config.chain_spec().is_adagio_active_at_timestamp(1000));
285
286        // Should be able to query activation condition
287        let activation = evm_config
288            .chain_spec()
289            .tempo_fork_activation(TempoHardfork::Adagio);
290        assert_eq!(activation, reth_chainspec::ForkCondition::Timestamp(0));
291    }
292}