1use crate::TempoEvmConfig;
2use alloy_consensus::crypto::RecoveryError;
3use alloy_primitives::Address;
4use reth_evm::{
5 ConfigureEngineEvm, ConfigureEvm, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor,
6 FromRecoveredTx, RecoveredTx, ToTxEnv, block::ExecutableTxParts,
7};
8use reth_primitives_traits::{SealedBlock, SignedTransaction};
9use std::sync::Arc;
10use tempo_payload_types::TempoExecutionData;
11use tempo_primitives::{Block, TempoTxEnvelope};
12use tempo_revm::TempoTxEnv;
13
14impl ConfigureEngineEvm<TempoExecutionData> for TempoEvmConfig {
15 fn evm_env_for_payload(
16 &self,
17 payload: &TempoExecutionData,
18 ) -> Result<EvmEnvFor<Self>, Self::Error> {
19 self.evm_env(&payload.block)
20 }
21
22 fn context_for_payload<'a>(
23 &self,
24 payload: &'a TempoExecutionData,
25 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
26 let TempoExecutionData {
27 block,
28 validator_set,
29 } = payload;
30 let mut context = self.context_for_block(block)?;
31
32 context.validator_set = validator_set.clone();
33
34 Ok(context)
35 }
36
37 fn tx_iterator_for_payload(
38 &self,
39 payload: &TempoExecutionData,
40 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
41 let block = payload.block.clone();
42 let transactions: Vec<_> = (0..payload.block.body().transactions.len())
43 .map(move |i| (block.clone(), i))
44 .collect();
45
46 Ok((transactions, RecoveredInBlock::new))
47 }
48}
49
50#[derive(Clone)]
54struct RecoveredInBlock {
55 block: Arc<SealedBlock<Block>>,
56 index: usize,
57 sender: Address,
58}
59
60impl RecoveredInBlock {
61 fn new((block, index): (Arc<SealedBlock<Block>>, usize)) -> Result<Self, RecoveryError> {
62 let sender = block.body().transactions[index].try_recover()?;
63 Ok(Self {
64 block,
65 index,
66 sender,
67 })
68 }
69}
70
71impl RecoveredTx<TempoTxEnvelope> for RecoveredInBlock {
72 fn tx(&self) -> &TempoTxEnvelope {
73 &self.block.body().transactions[self.index]
74 }
75
76 fn signer(&self) -> &alloy_primitives::Address {
77 &self.sender
78 }
79}
80
81impl ToTxEnv<TempoTxEnv> for RecoveredInBlock {
82 fn to_tx_env(&self) -> TempoTxEnv {
83 TempoTxEnv::from_recovered_tx(self.tx(), *self.signer())
84 }
85}
86
87impl ExecutableTxParts<TempoTxEnv, TempoTxEnvelope> for RecoveredInBlock {
88 type Recovered = Self;
89
90 fn into_parts(self) -> (TempoTxEnv, Self::Recovered) {
91 (self.to_tx_env(), self)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use alloy_consensus::{BlockHeader, Signed, TxLegacy};
99 use alloy_primitives::{B256, Bytes, Signature, TxKind, U256};
100 use alloy_rlp::{Encodable, bytes::BytesMut};
101 use rayon::iter::{IntoParallelIterator, ParallelIterator};
102 use reth_chainspec::EthChainSpec;
103 use reth_evm::{ConfigureEngineEvm, ConvertTx, ExecutableTxTuple};
104 use tempo_chainspec::{TempoChainSpec, spec::ANDANTINO};
105 use tempo_primitives::{
106 BlockBody, SubBlockMetadata, TempoHeader, transaction::envelope::TEMPO_SYSTEM_TX_SIGNATURE,
107 };
108
109 fn create_legacy_tx() -> TempoTxEnvelope {
110 let tx = TxLegacy {
111 chain_id: Some(1),
112 nonce: 0,
113 gas_price: 1,
114 gas_limit: 21000,
115 to: TxKind::Call(Address::repeat_byte(0x01)),
116 value: U256::ZERO,
117 input: Bytes::new(),
118 };
119 TempoTxEnvelope::Legacy(Signed::new_unhashed(tx, Signature::test_signature()))
120 }
121
122 fn create_subblock_metadata_tx(chain_id: u64, block_number: u64) -> TempoTxEnvelope {
123 let metadata: Vec<SubBlockMetadata> = vec![];
124 let mut input = BytesMut::new();
125 metadata.encode(&mut input);
126 input.extend_from_slice(&U256::from(block_number).to_be_bytes::<32>());
127
128 TempoTxEnvelope::Legacy(Signed::new_unhashed(
129 TxLegacy {
130 chain_id: Some(chain_id),
131 nonce: 0,
132 gas_price: 0,
133 gas_limit: 0,
134 to: TxKind::Call(Address::ZERO),
135 value: U256::ZERO,
136 input: input.freeze().into(),
137 },
138 TEMPO_SYSTEM_TX_SIGNATURE,
139 ))
140 }
141
142 fn create_test_block(transactions: Vec<TempoTxEnvelope>) -> Arc<SealedBlock<Block>> {
143 let header = TempoHeader {
144 inner: alloy_consensus::Header {
145 number: 1,
146 timestamp: 1000,
147 gas_limit: 30_000_000,
148 parent_beacon_block_root: Some(B256::ZERO),
149 ..Default::default()
150 },
151 general_gas_limit: 10_000_000,
152 timestamp_millis_part: 500,
153 shared_gas_limit: 3_000_000,
154 };
155
156 let body = BlockBody {
157 transactions,
158 ommers: vec![],
159 withdrawals: None,
160 };
161
162 let block = Block { header, body };
163 Arc::new(SealedBlock::seal_slow(block))
164 }
165
166 #[test]
167 fn test_tx_iterator_for_payload() {
168 let chainspec = Arc::new(TempoChainSpec::from_genesis(ANDANTINO.genesis().clone()));
169 let evm_config = TempoEvmConfig::new(chainspec.clone());
170
171 let tx1 = create_legacy_tx();
172 let tx2 = create_legacy_tx();
173 let system_tx = create_subblock_metadata_tx(chainspec.chain().id(), 1);
174
175 let block = create_test_block(vec![tx1, tx2, system_tx]);
176
177 let payload = TempoExecutionData {
178 block,
179 validator_set: None,
180 };
181
182 let result = evm_config.tx_iterator_for_payload(&payload);
183 assert!(result.is_ok());
184
185 let tuple = result.unwrap();
186 let (iter, recover_fn) = tuple.into_parts();
187 let items: Vec<_> = iter.into_par_iter().collect();
188
189 assert_eq!(items.len(), 3);
191
192 for item in items {
194 let recovered = recover_fn.convert(item);
195 assert!(recovered.is_ok());
196 }
197 }
198
199 #[test]
200 fn test_context_for_payload() {
201 let chainspec = Arc::new(TempoChainSpec::from_genesis(ANDANTINO.genesis().clone()));
202 let evm_config = TempoEvmConfig::new(chainspec.clone());
203
204 let system_tx = create_subblock_metadata_tx(chainspec.chain().id(), 1);
205 let block = create_test_block(vec![system_tx]);
206 let validator_set = Some(vec![B256::repeat_byte(0x01), B256::repeat_byte(0x02)]);
207
208 let payload = TempoExecutionData {
209 block,
210 validator_set: validator_set.clone(),
211 };
212
213 let result = evm_config.context_for_payload(&payload);
214 assert!(result.is_ok());
215
216 let context = result.unwrap();
217
218 assert_eq!(context.general_gas_limit, 10_000_000);
220 assert_eq!(context.shared_gas_limit, 3_000_000);
221 assert_eq!(context.validator_set, validator_set);
222 assert!(context.subblock_fee_recipients.is_empty());
223 }
224
225 #[test]
226 fn test_evm_env_for_payload() {
227 let chainspec = Arc::new(TempoChainSpec::from_genesis(ANDANTINO.genesis().clone()));
228 let evm_config = TempoEvmConfig::new(chainspec.clone());
229
230 let system_tx = create_subblock_metadata_tx(chainspec.chain().id(), 1);
231 let block = create_test_block(vec![system_tx]);
232
233 let payload = TempoExecutionData {
234 block: block.clone(),
235 validator_set: None,
236 };
237
238 let result = evm_config.evm_env_for_payload(&payload);
239 assert!(result.is_ok());
240
241 let evm_env = result.unwrap();
242
243 assert_eq!(evm_env.block_env.inner.number, U256::from(block.number()));
245 assert_eq!(
246 evm_env.block_env.inner.timestamp,
247 U256::from(block.timestamp())
248 );
249 assert_eq!(
250 evm_env.block_env.inner.gas_limit,
251 block.header().gas_limit()
252 );
253 assert_eq!(evm_env.block_env.timestamp_millis_part, 500);
254 }
255}