1#![doc(
10 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
11 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
12 issue_tracker_base_url = "https://github.com/SeismicSystems/seismic-reth/issues/"
13)]
14#![cfg_attr(not(test), warn(unused_crate_dependencies))]
15#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use core::{convert::Infallible, net::IpAddr};
21
22use alloc::{sync::Arc, vec::Vec};
23use alloy_consensus::{transaction::TxSeismicElements, Header, TxSeismic, Typed2718};
24use alloy_primitives::{Address, Bytes, TxHash, TxKind, U256};
25use reth_chainspec::{ChainSpec, Head};
26use reth_enclave::{EnclaveClient, EnclaveError, SchnorrkelKeypair, SyncEnclaveApiClient};
27use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
28use reth_primitives::{transaction::FillTxEnv, Transaction, TransactionSigned};
29use reth_tracing::tracing::debug;
30use revm_primitives::{
31 AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EVMError,
32 EVMResultGeneric, Env, SpecId, TxEnv,
33};
34
35mod config;
36use alloy_eips::eip1559::INITIAL_BASE_FEE;
37pub use config::{revm_spec, revm_spec_by_timestamp_after_merge};
38use reth_ethereum_forks::EthereumHardfork;
39use secp256k1::PublicKey;
40
41pub mod execute;
42
43pub mod dao_fork;
45
46pub mod eip6110;
48
49#[derive(Debug, Clone)]
51pub struct EthEvmConfig {
52 chain_spec: Arc<ChainSpec>,
53 enclave_client: EnclaveClient,
54}
55
56impl EthEvmConfig {
57 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
59 Self { chain_spec, enclave_client: EnclaveClient::default() }
60 }
61
62 pub fn new_with_enclave_client(
64 chain_spec: Arc<ChainSpec>,
65 enclave_client: EnclaveClient,
66 ) -> Self {
67 Self { chain_spec, enclave_client }
68 }
69
70 pub fn new_with_enclave_addr_port(
73 chain_spec: Arc<ChainSpec>,
74 enclave_addr: IpAddr,
75 enclave_port: u16,
76 enclave_server_timeout: u64,
77 ) -> Self {
78 debug!(target: "reth::evm", ?enclave_addr, ?enclave_port, "Creating new enclave client");
79
80 let enclave_client = EnclaveClient::builder()
81 .addr(enclave_addr.to_string())
82 .port(enclave_port)
83 .timeout(std::time::Duration::from_secs(enclave_server_timeout))
84 .build();
85 Self::new_with_enclave_client(chain_spec, enclave_client)
86 }
87
88 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
90 &self.chain_spec
91 }
92
93 pub fn recover_pubkey(&self, tx: &TransactionSigned) -> Option<PublicKey> {
95 let signature_hash = tx.signature_hash();
96
97 tx.signature.recover_from_prehash(&signature_hash).ok().and_then(|verifying_key| {
98 let pbk_bytes = verifying_key.to_encoded_point(false);
99 PublicKey::from_slice(pbk_bytes.as_bytes()).ok()
100 })
101 }
102}
103
104impl ConfigureEvmEnv for EthEvmConfig {
105 type Header = Header;
106 type Transaction = TransactionSigned;
107 type Error = Infallible;
108
109 fn encrypt(
110 &self,
111 data: &Bytes,
112 seismic_elements: &TxSeismicElements,
113 ) -> EVMResultGeneric<Bytes, EnclaveError> {
114 Ok(seismic_elements
115 .server_encrypt(&self.enclave_client, &data)
116 .map_err(|_| EVMError::Database(EnclaveError::EncryptionError))?)
117 }
118 fn decrypt(
119 &self,
120 data: &Bytes,
121 seismic_elements: &TxSeismicElements,
122 ) -> EVMResultGeneric<Bytes, EnclaveError> {
123 Ok(seismic_elements
124 .server_decrypt(&self.enclave_client, &data)
125 .map_err(|_| EVMError::Database(EnclaveError::DecryptionError))?)
126 }
127
128 fn get_eph_rng_keypair(&self) -> EVMResultGeneric<SchnorrkelKeypair, EnclaveError> {
130 Ok(self.enclave_client.get_eph_rng_keypair().map_err(|e| {
131 EVMError::Database(EnclaveError::EphRngKeypairGenerationError(e.to_string()))
132 })?)
133 }
134
135 fn fill_seismic_tx_env(
137 &self,
138 tx_env: &mut TxEnv,
139 tx: &TxSeismic,
140 sender: Address,
141 tx_hash: TxHash,
142 ) -> EVMResultGeneric<(), EnclaveError> {
143 let enclave_decryption = self.decrypt(&tx.input, &tx.seismic_elements)?;
144
145 let data = Bytes::from(enclave_decryption.clone());
146
147 tx_env.caller = sender;
148 tx_env.gas_limit = tx.gas_limit;
149 tx_env.gas_price = U256::from(tx.gas_price);
150 tx_env.gas_priority_fee = None;
151 tx_env.transact_to = tx.to;
152 tx_env.value = tx.value;
153 tx_env.data = data;
154 tx_env.chain_id = Some(tx.chain_id);
155 tx_env.nonce = Some(tx.nonce);
156 tx_env.access_list.clear();
157 tx_env.blob_hashes.clear();
158 tx_env.max_fee_per_blob_gas.take();
159 tx_env.authorization_list = None;
160 tx_env.tx_hash = tx_hash;
161 tx_env.tx_type = Some(tx.ty() as isize);
162 Ok(())
163 }
164
165 fn fill_tx_env(
166 &self,
167 tx_env: &mut TxEnv,
168 transaction: &TransactionSigned,
169 sender: Address,
170 ) -> EVMResultGeneric<(), EnclaveError> {
171 match &transaction.transaction {
172 Transaction::Seismic(tx) => {
173 self.fill_seismic_tx_env(tx_env, tx, sender, transaction.hash())?;
174 Ok(())
175 }
176 _ => Ok(transaction.fill_tx_env(tx_env, sender)),
177 }
178 }
179
180 fn fill_tx_env_system_contract_call(
181 &self,
182 env: &mut Env,
183 caller: Address,
184 contract: Address,
185 data: Bytes,
186 ) {
187 #[allow(clippy::needless_update)] let tx = TxEnv {
189 caller,
190 transact_to: TxKind::Call(contract),
191 nonce: None,
193 gas_limit: 30_000_000,
194 value: U256::ZERO,
195 data,
196 gas_price: U256::ZERO,
199 chain_id: None,
201 gas_priority_fee: None,
204 access_list: Vec::new(),
205 blob_hashes: Vec::new(),
207 max_fee_per_blob_gas: None,
208 ..Default::default()
210 };
211 env.tx = tx;
212
213 env.block.gas_limit = U256::from(env.tx.gas_limit);
215
216 env.block.basefee = U256::ZERO;
218 }
219
220 fn fill_cfg_env(
221 &self,
222 cfg_env: &mut CfgEnvWithHandlerCfg,
223 header: &Header,
224 total_difficulty: U256,
225 ) {
226 let spec_id = config::revm_spec(
227 self.chain_spec(),
228 &Head {
229 number: header.number,
230 timestamp: header.timestamp,
231 difficulty: header.difficulty,
232 total_difficulty,
233 hash: Default::default(),
234 },
235 );
236
237 cfg_env.chain_id = self.chain_spec.chain().id();
238 cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse;
239
240 cfg_env.handler_cfg.spec_id = spec_id;
241 }
242
243 fn next_cfg_and_block_env(
244 &self,
245 parent: &Self::Header,
246 attributes: NextBlockEnvAttributes,
247 ) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error> {
248 let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id());
250
251 let spec_id = revm_spec_by_timestamp_after_merge(&self.chain_spec, attributes.timestamp);
253
254 let blob_excess_gas_and_price = parent
257 .next_block_excess_blob_gas()
258 .or_else(|| (spec_id == SpecId::CANCUN).then_some(0))
259 .map(BlobExcessGasAndPrice::new);
260
261 let mut basefee = parent.next_block_base_fee(
262 self.chain_spec.base_fee_params_at_timestamp(attributes.timestamp),
263 );
264
265 let mut gas_limit = U256::from(parent.gas_limit);
266
267 if self.chain_spec.fork(EthereumHardfork::London).transitions_at_block(parent.number + 1) {
270 let elasticity_multiplier = self
271 .chain_spec
272 .base_fee_params_at_timestamp(attributes.timestamp)
273 .elasticity_multiplier;
274
275 gas_limit *= U256::from(elasticity_multiplier);
277
278 basefee = Some(INITIAL_BASE_FEE)
280 }
281
282 let block_env = BlockEnv {
283 number: U256::from(parent.number + 1),
284 coinbase: attributes.suggested_fee_recipient,
285 timestamp: U256::from(attributes.timestamp),
286 difficulty: U256::ZERO,
287 prevrandao: Some(attributes.prev_randao),
288 gas_limit,
289 basefee: basefee.map(U256::from).unwrap_or_default(),
291 blob_excess_gas_and_price,
293 };
294
295 Ok((CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env))
296 }
297}
298
299impl ConfigureEvm for EthEvmConfig {
300 type DefaultExternalContext<'a> = ();
301
302 fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a> {}
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use alloy_consensus::{constants::KECCAK_EMPTY, Header};
309 use alloy_genesis::Genesis;
310 use alloy_primitives::{B256, U256};
311 use reth_chainspec::{Chain, ChainSpec, MAINNET};
312 use reth_enclave::start_mock_enclave_server_random_port;
313 use reth_evm::execute::ProviderError;
314 use reth_revm::{
315 db::{CacheDB, EmptyDBTyped},
316 handler::register::EvmHandler,
317 inspectors::NoOpInspector,
318 precompile::u64_to_address,
319 primitives::{BlockEnv, CfgEnv, SpecId},
320 Evm, Handler, JournaledState,
321 };
322 use revm_primitives::{EnvWithHandlerCfg, HandlerCfg};
323 use std::collections::HashSet;
324
325 #[test]
326 fn test_fill_cfg_and_block_env() {
327 let header = Header::default();
329
330 let chain_spec = ChainSpec::builder()
333 .chain(Chain::mainnet())
334 .genesis(Genesis::default())
335 .london_activated()
336 .paris_activated()
337 .shanghai_activated()
338 .build();
339
340 let total_difficulty = U256::ZERO;
342
343 let (cfg_env, _) = EthEvmConfig::new(Arc::new(chain_spec.clone()))
346 .cfg_and_block_env(&header, total_difficulty);
347
348 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
351 }
352
353 #[test]
354 #[allow(clippy::needless_update)]
355 fn test_evm_configure() {
356 let evm_config = EthEvmConfig::new(MAINNET.clone());
358
359 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
361
362 let evm = evm_config.evm(db);
364
365 assert_eq!(evm.context.evm.inner.env, Box::default());
367
368 assert_eq!(
370 evm.context.evm.inner.journaled_state,
371 JournaledState::new(SpecId::LATEST, HashSet::default())
372 );
373
374 assert!(evm.context.evm.inner.db.accounts.is_empty());
376
377 assert!(evm.context.evm.inner.db.block_hashes.is_empty());
379
380 assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
382 assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
383 assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
384
385 assert!(evm.context.evm.inner.db.logs.is_empty());
387
388 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
390 }
391
392 #[test]
393 #[allow(clippy::needless_update)]
394 fn test_evm_with_env_default_spec() {
395 let evm_config = EthEvmConfig::new(MAINNET.clone());
396
397 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
398
399 let env_with_handler = EnvWithHandlerCfg::default();
400
401 let evm = evm_config.evm_with_env(db, env_with_handler.clone());
402
403 assert_eq!(evm.context.evm.env, env_with_handler.env);
405
406 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
408
409 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
411 }
412
413 #[test]
414 #[allow(clippy::needless_update)]
415 fn test_evm_with_env_custom_cfg() {
416 let evm_config = EthEvmConfig::new(MAINNET.clone());
417
418 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
419
420 let cfg = CfgEnv::default().with_chain_id(111);
422
423 let env_with_handler = EnvWithHandlerCfg {
424 env: Box::new(Env {
425 cfg: cfg.clone(),
426 block: BlockEnv::default(),
427 tx: TxEnv::default(),
428 }),
429 handler_cfg: Default::default(),
430 };
431
432 let evm = evm_config.evm_with_env(db, env_with_handler);
433
434 assert_eq!(evm.context.evm.inner.env.cfg, cfg);
436
437 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
439
440 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
442 }
443
444 #[test]
445 #[allow(clippy::needless_update)]
446 fn test_evm_with_env_custom_block_and_tx() {
447 let evm_config = EthEvmConfig::new(MAINNET.clone());
450
451 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
452
453 let block = BlockEnv {
455 basefee: U256::from(1000),
456 gas_limit: U256::from(10_000_000),
457 number: U256::from(42),
458 ..Default::default()
459 };
460 let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
461
462 let env_with_handler = EnvWithHandlerCfg {
463 env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
464 handler_cfg: Default::default(),
465 };
466
467 let evm = evm_config.evm_with_env(db, env_with_handler.clone());
468
469 assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
471 assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
472
473 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
475
476 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
478 }
479
480 #[test]
481 #[allow(clippy::needless_update)]
482 fn test_evm_with_spec_id() {
483 let evm_config = EthEvmConfig::new(MAINNET.clone());
484
485 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
486
487 let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
488
489 let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
490
491 let evm = evm_config.evm_with_env(db, env_with_handler);
492
493 assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
495
496 assert_eq!(
498 evm.handler.cfg,
499 HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
500 );
501 }
502
503 #[test]
504 #[allow(clippy::needless_update)]
505 fn test_evm_with_inspector() {
506 let evm_config = EthEvmConfig::new(MAINNET.clone());
507
508 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
509
510 let noop = NoOpInspector;
512
513 let evm = evm_config.evm_with_inspector(db, noop);
514
515 assert_eq!(evm.context.external, noop);
517
518 assert_eq!(evm.context.evm.inner.env, Box::default());
520
521 assert_eq!(
523 evm.context.evm.inner.journaled_state,
524 JournaledState::new(SpecId::LATEST, HashSet::default())
525 );
526
527 assert!(evm.context.evm.inner.db.accounts.is_empty());
529
530 assert!(evm.context.evm.inner.db.block_hashes.is_empty());
532
533 assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
535 assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
536 assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
537
538 assert!(evm.context.evm.inner.db.logs.is_empty());
540
541 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
543 }
544
545 #[test]
546 #[allow(clippy::needless_update)]
547 fn test_evm_with_env_and_default_inspector() {
548 let evm_config = EthEvmConfig::new(MAINNET.clone());
549 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
550
551 let env_with_handler = EnvWithHandlerCfg::default();
552
553 let evm =
554 evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
555
556 assert_eq!(evm.context.evm.env, env_with_handler.env);
558 assert_eq!(evm.context.external, NoOpInspector);
559 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
560
561 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
563 }
564
565 #[test]
566 #[allow(clippy::needless_update)]
567 fn test_evm_with_env_inspector_and_custom_cfg() {
568 let evm_config = EthEvmConfig::new(MAINNET.clone());
569 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
570
571 let cfg = CfgEnv::default().with_chain_id(111);
572 let block = BlockEnv::default();
573 let tx = TxEnv::default();
574 let env_with_handler = EnvWithHandlerCfg {
575 env: Box::new(Env { cfg: cfg.clone(), block, tx }),
576 handler_cfg: Default::default(),
577 };
578
579 let evm = evm_config.evm_with_env_and_inspector(db, env_with_handler, NoOpInspector);
580
581 assert_eq!(evm.context.evm.env.cfg, cfg);
583 assert_eq!(evm.context.external, NoOpInspector);
584 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
585
586 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
588 }
589
590 #[test]
591 #[allow(clippy::needless_update)]
592 fn test_evm_with_env_inspector_and_custom_block_tx() {
593 let evm_config = EthEvmConfig::new(MAINNET.clone());
594 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
595
596 let block = BlockEnv {
598 basefee: U256::from(1000),
599 gas_limit: U256::from(10_000_000),
600 number: U256::from(42),
601 ..Default::default()
602 };
603 let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
604 let env_with_handler = EnvWithHandlerCfg {
605 env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
606 handler_cfg: Default::default(),
607 };
608
609 let evm =
610 evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
611
612 assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
614 assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
615 assert_eq!(evm.context.external, NoOpInspector);
616 assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
617
618 assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
620 }
621
622 #[test]
623 #[allow(clippy::needless_update)]
624 fn test_evm_with_env_inspector_and_spec_id() {
625 let evm_config = EthEvmConfig::new(MAINNET.clone());
626 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
627
628 let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
629 let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
630
631 let evm =
632 evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
633
634 assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
636 assert_eq!(evm.context.evm.env, env_with_handler.env);
637 assert_eq!(evm.context.external, NoOpInspector);
638
639 assert_eq!(
641 evm.handler.cfg,
642 HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
643 );
644 }
645
646 #[tokio::test(flavor = "multi_thread")]
647 #[allow(clippy::needless_update)]
648 async fn test_evm_with_spec_id_seismic() {
649 let enclave_client = start_mock_enclave_server_random_port().await;
651
652 let evm_config = EthEvmConfig::new_with_enclave_client(MAINNET.clone(), enclave_client);
653
654 let db = CacheDB::<EmptyDBTyped<Infallible>>::default();
655
656 let handler_cfg = HandlerCfg { spec_id: SpecId::MERCURY, ..Default::default() };
657
658 let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
659
660 let evm = evm_config.evm_with_env(db.clone(), env_with_handler);
661
662 assert_eq!(evm.handler.spec_id(), SpecId::MERCURY);
664 assert!(evm.handler.is_seismic());
665
666 type DB = CacheDB<EmptyDBTyped<Infallible>>;
668 type EXT = ();
669
670 let seismic_handler: Handler<'_, reth_revm::Context<EXT, DB>, EXT, DB> =
671 EvmHandler::seismic_with_spec(SpecId::MERCURY);
672 let seismic_evm = Evm::builder().with_db(db).with_handler(seismic_handler).build();
673
674 let precompile_addresses =
675 [u64_to_address(101), u64_to_address(102), u64_to_address(103), u64_to_address(104)];
676
677 let precompiles = seismic_evm.handler.pre_execution().load_precompiles();
678
679 for &addr in &precompile_addresses {
680 let is_contained = precompiles.contains(&addr);
681 assert!(
682 is_contained,
683 "Expected Precompile at address for standard evm generation {addr:?}"
684 );
685 }
686 assert_eq!(evm.handler.spec_id(), seismic_evm.handler.spec_id());
687 assert_eq!(evm.handler.is_seismic(), seismic_evm.handler.is_seismic());
688
689 let precompiles = evm.handler.pre_execution().load_precompiles();
691
692 for &addr in &precompile_addresses {
693 let is_contained = precompiles.contains(&addr);
694 assert!(
695 is_contained,
696 "Expected Precompile at address for RETH evm generation {addr:?}"
697 );
698 }
699 }
700}