1#![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#[derive(Debug, Clone)]
46pub struct TempoEvmConfig {
47 pub inner: EthEvmConfig<TempoChainSpec, TempoEvmFactory>,
49
50 pub block_assembler: TempoBlockAssembler,
52}
53
54impl TempoEvmConfig {
55 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 pub fn new_with_default_factory(chain_spec: Arc<TempoChainSpec>) -> Self {
66 Self::new(chain_spec, TempoEvmFactory::default())
67 }
68
69 pub const fn chain_spec(&self) -> &Arc<TempoChainSpec> {
71 self.inner.chain_spec()
72 }
73
74 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 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 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 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 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 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 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 let activation = evm_config
288 .chain_spec()
289 .tempo_fork_activation(TempoHardfork::Adagio);
290 assert_eq!(activation, reth_chainspec::ForkCondition::Timestamp(0));
291 }
292}