diff --git a/Cargo.lock b/Cargo.lock index 838a95ac93..884c8b5d5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,6 +1068,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-where" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "derive_arbitrary" version = "1.3.2" @@ -3114,9 +3125,10 @@ dependencies = [ "alloy-transport", "anyhow", "auto_impl", - "cfg-if", "criterion", + "derive-where", "dyn-clone", + "enumn", "ethers-contract", "ethers-core", "ethers-providers", @@ -3135,6 +3147,7 @@ name = "revm-interpreter" version = "6.0.0" dependencies = [ "bincode", + "derive-where", "paste", "phf", "revm-primitives", @@ -3178,6 +3191,7 @@ dependencies = [ "bitvec", "c-kzg", "cfg-if", + "derive-where", "derive_more", "dyn-clone", "enumn", diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 50a75a8deb..b73abbeea6 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -2,8 +2,8 @@ use revm::{ db::BenchmarkDB, inspector_handle_register, inspectors::TracerEip3155, - primitives::{Address, Bytecode, TxKind}, - Evm, + primitives::{address, Address, Bytecode, TxKind}, + Database, Evm, }; use std::io::Error as IoError; use std::path::PathBuf; @@ -61,6 +61,8 @@ pub struct Cmd { impl Cmd { /// Run statetest command. pub fn run(&self) -> Result<(), Errors> { + const CALLER: Address = address!("0000000000000000000000000000000000000001"); + let bytecode_str: Cow<'_, str> = if let Some(path) = &self.path { // check if path exists. if !path.exists() { @@ -75,19 +77,21 @@ impl Cmd { let input = hex::decode(self.input.trim()) .map_err(|_| Errors::InvalidInput)? .into(); + + let mut db = BenchmarkDB::new_bytecode(Bytecode::new_raw(bytecode.into())); + + let nonce = db.basic(CALLER).unwrap().map_or(0, |account| account.nonce); + // BenchmarkDB is dummy state that implements Database trait. // the bytecode is deployed at zero address. let mut evm = Evm::builder() - .with_db(BenchmarkDB::new_bytecode(Bytecode::new_raw( - bytecode.into(), - ))) + .with_db(db) .modify_tx_env(|tx| { // execution globals block hash/gas_limit/coinbase/timestamp.. - tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); + tx.caller = CALLER; tx.transact_to = TxKind::Call(Address::ZERO); tx.data = input; + tx.nonce = nonce; }) .build(); diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 48c62f35fe..dc165c184c 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -28,6 +28,11 @@ use std::{ use thiserror::Error; use walkdir::{DirEntry, WalkDir}; +#[cfg(feature = "optimism")] +type TestChainSpec = revm::optimism::OptimismChainSpec; +#[cfg(not(feature = "optimism"))] +type TestChainSpec = revm::primitives::EthChainSpec; + #[derive(Debug, Error)] #[error("Test {name} failed: {kind}")] pub struct TestError { @@ -130,8 +135,8 @@ fn check_evm_execution( test: &Test, expected_output: Option<&Bytes>, test_name: &str, - exec_result: &EVMResultGeneric, - evm: &Evm<'_, EXT, &mut State>, + exec_result: &EVMResultGeneric, TestChainSpec, Infallible>, + evm: &Evm<'_, TestChainSpec, EXT, &mut State>, print_json_outcome: bool, ) -> Result<(), TestError> { let logs_root = log_rlp_hash(exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); @@ -155,7 +160,7 @@ fn check_evm_execution( Err(e) => e.to_string(), }, "postLogsHash": logs_root, - "fork": evm.handler.cfg().spec_id, + "fork": evm.handler.spec_id(), "test": test_name, "d": test.indexes.data, "g": test.indexes.gas, @@ -273,7 +278,7 @@ pub fn execute_test_suite( cache_state.insert_account_with_storage(address, acc_info, info.storage); } - let mut env = Box::::default(); + let mut env = Box::>::default(); // for mainnet env.cfg.chain_id = 1; // env.cfg.spec_id is set down the road @@ -343,6 +348,8 @@ pub fn execute_test_suite( .get(test.indexes.data) .unwrap() .clone(); + + env.tx.nonce = u64::try_from(unit.transaction.nonce).unwrap(); env.tx.value = unit.transaction.value[test.indexes.value]; env.tx.access_list = unit @@ -360,10 +367,7 @@ pub fn execute_test_suite( env.tx.transact_to = to; let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled( - spec_id, - revm::primitives::SpecId::SPURIOUS_DRAGON, - )); + cache.set_state_clear_flag(SpecId::enabled(spec_id, SpecId::SPURIOUS_DRAGON)); let mut state = revm::db::State::builder() .with_cached_prestate(cache) .with_bundle_update() @@ -429,10 +433,7 @@ pub fn execute_test_suite( // re build to run with tracing let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled( - spec_id, - revm::primitives::SpecId::SPURIOUS_DRAGON, - )); + cache.set_state_clear_flag(SpecId::enabled(spec_id, SpecId::SPURIOUS_DRAGON)); let state = revm::db::State::builder() .with_cached_prestate(cache) .with_bundle_update() diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 91777aa638..1629b4cdf1 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -24,6 +24,7 @@ all = "warn" [dependencies] revm-primitives = { path = "../primitives", version = "5.0.0", default-features = false } +derive-where = { version = "1.2.7", default-features = false } paste = { version = "1.0", optional = true } phf = { version = "0.11", default-features = false, optional = true, features = [ "macros", @@ -56,14 +57,6 @@ portable = ["revm-primitives/portable"] parse = ["dep:paste", "dep:phf"] optimism = ["revm-primitives/optimism"] -# Optimism default handler enabled Optimism handler register by default in EvmBuilder. -optimism-default-handler = [ - "optimism", - "revm-primitives/optimism-default-handler", -] -negate-optimism-default-handler = [ - "revm-primitives/negate-optimism-default-handler", -] dev = [ "memory_limit", @@ -73,6 +66,7 @@ dev = [ "optional_gas_refund", "optional_no_base_fee", "optional_beneficiary_reward", + "optional_nonce_check", ] memory_limit = ["revm-primitives/memory_limit"] optional_balance_check = ["revm-primitives/optional_balance_check"] @@ -81,3 +75,4 @@ optional_eip3607 = ["revm-primitives/optional_eip3607"] optional_gas_refund = ["revm-primitives/optional_gas_refund"] optional_no_base_fee = ["revm-primitives/optional_no_base_fee"] optional_beneficiary_reward = ["revm-primitives/optional_beneficiary_reward"] +optional_nonce_check = ["revm-primitives/optional_nonce_check"] diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 6befc58f06..0be8646bf6 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,9 +1,7 @@ -use revm_primitives::AccessListItem; - use super::constants::*; use crate::{ num_words, - primitives::{SpecId, U256}, + primitives::{AccessListItem, SpecId, U256}, SelfDestructResult, }; diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index e07cb31637..4dc65b5172 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -2,14 +2,18 @@ use crate::primitives::{Address, Bytes, Env, Log, B256, U256}; mod dummy; pub use dummy::DummyHost; +use revm_primitives::ChainSpec; /// EVM context host. pub trait Host { + /// Chain specification. + type ChainSpecT: ChainSpec; + /// Returns a reference to the environment. - fn env(&self) -> &Env; + fn env(&self) -> &Env; /// Returns a mutable reference to the environment. - fn env_mut(&mut self) -> &mut Env; + fn env_mut(&mut self) -> &mut Env; /// Load an account. /// @@ -84,13 +88,15 @@ pub struct SelfDestructResult { #[cfg(test)] mod tests { + use revm_primitives::EthChainSpec; + use super::*; fn assert_host() {} #[test] fn object_safety() { - assert_host::(); - assert_host::(); + assert_host::>(); + assert_host::>(); } } diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index 77993b87ef..e06d20a9d2 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -1,5 +1,9 @@ +use derive_where::derive_where; + use crate::{ - primitives::{hash_map::Entry, Address, Bytes, Env, HashMap, Log, B256, KECCAK_EMPTY, U256}, + primitives::{ + hash_map::Entry, Address, Bytes, ChainSpec, Env, HashMap, Log, B256, KECCAK_EMPTY, U256, + }, Host, SStoreResult, SelfDestructResult, }; use std::vec::Vec; @@ -7,21 +11,29 @@ use std::vec::Vec; use super::LoadAccountResult; /// A dummy [Host] implementation. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct DummyHost { - pub env: Env, +#[derive_where(Clone, Debug, Default; ChainSpecT::Block, ChainSpecT::Transaction)] +pub struct DummyHost +where + ChainSpecT: ChainSpec, +{ + pub env: Env, pub storage: HashMap, pub transient_storage: HashMap, pub log: Vec, } -impl DummyHost { +impl DummyHost +where + ChainSpecT: ChainSpec, +{ /// Create a new dummy host with the given [`Env`]. #[inline] - pub fn new(env: Env) -> Self { + pub fn new(env: Env) -> Self { Self { env, - ..Default::default() + storage: HashMap::new(), + transient_storage: HashMap::new(), + log: Vec::new(), } } @@ -33,14 +45,19 @@ impl DummyHost { } } -impl Host for DummyHost { +impl Host for DummyHost +where + ChainSpecT: ChainSpec, +{ + type ChainSpecT = ChainSpecT; + #[inline] - fn env(&self) -> &Env { + fn env(&self) -> &Env { &self.env } #[inline] - fn env_mut(&mut self) -> &mut Env { + fn env_mut(&mut self) -> &mut Env { &mut self.env } diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index aed0da331e..2507f4a21f 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -1,3 +1,8 @@ +use core::fmt::Debug; + +use derive_where::derive_where; +use revm_primitives::ChainSpec; + use crate::primitives::{HaltReason, OutOfGasError, SuccessReason}; #[repr(u8)] @@ -105,8 +110,6 @@ impl From for InstructionResult { HaltReason::EofAuxDataOverflow => Self::EofAuxDataOverflow, HaltReason::EofAuxDataTooSmall => Self::EofAuxDataTooSmall, HaltReason::EOFFunctionStackOverflow => Self::EOFFunctionStackOverflow, - #[cfg(feature = "optimism")] - HaltReason::FailedDeposit => Self::FatalExternalError, } } } @@ -197,16 +200,17 @@ pub enum InternalResult { CreateInitCodeStartingEF00, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum SuccessOrHalt { +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive_where(Debug; ChainSpecT::HaltReason)] +pub enum SuccessOrHalt { Success(SuccessReason), Revert, - Halt(HaltReason), + Halt(ChainSpecT::HaltReason), FatalExternalError, Internal(InternalResult), } -impl SuccessOrHalt { +impl SuccessOrHalt { /// Returns true if the transaction returned successfully without halts. #[inline] pub fn is_success(self) -> bool { @@ -236,7 +240,7 @@ impl SuccessOrHalt { /// Returns the [HaltReason] value the EVM has experienced an exceptional halt #[inline] - pub fn to_halt(self) -> Option { + pub fn to_halt(self) -> Option { match self { SuccessOrHalt::Halt(reason) => Some(reason), _ => None, @@ -244,7 +248,7 @@ impl SuccessOrHalt { } } -impl From for SuccessOrHalt { +impl From for SuccessOrHalt { fn from(result: InstructionResult) -> Self { match result { InstructionResult::Continue => Self::Internal(InternalResult::InternalContinue), // used only in interpreter loop @@ -254,55 +258,65 @@ impl From for SuccessOrHalt { InstructionResult::Revert => Self::Revert, InstructionResult::CreateInitCodeStartingEF00 => Self::Revert, InstructionResult::CallOrCreate => Self::Internal(InternalResult::InternalCallOrCreate), // used only in interpreter loop - InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep), // not gonna happen for first call - InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds), // Check for first call is done separately. - InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic)), + InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep.into()), // not gonna happen for first call + InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds.into()), // Check for first call is done separately. + InstructionResult::OutOfGas => { + Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic).into()) + } InstructionResult::MemoryLimitOOG => { - Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit)) + Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit).into()) + } + InstructionResult::MemoryOOG => { + Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory).into()) } - InstructionResult::MemoryOOG => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory)), InstructionResult::PrecompileOOG => { - Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile)) + Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile).into()) } InstructionResult::InvalidOperandOOG => { - Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand)) + Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand).into()) } InstructionResult::OpcodeNotFound | InstructionResult::ReturnContractInNotInitEOF => { - Self::Halt(HaltReason::OpcodeNotFound) + Self::Halt(HaltReason::OpcodeNotFound.into()) } InstructionResult::CallNotAllowedInsideStatic => { - Self::Halt(HaltReason::CallNotAllowedInsideStatic) + Self::Halt(HaltReason::CallNotAllowedInsideStatic.into()) } // first call is not static call InstructionResult::StateChangeDuringStaticCall => { - Self::Halt(HaltReason::StateChangeDuringStaticCall) + Self::Halt(HaltReason::StateChangeDuringStaticCall.into()) } - InstructionResult::InvalidEFOpcode => Self::Halt(HaltReason::InvalidEFOpcode), - InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump), - InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated), - InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow), - InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow), - InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset), - InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision), - InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment), // Check for first call is done separately. - InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError), - InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow), + InstructionResult::InvalidEFOpcode => Self::Halt(HaltReason::InvalidEFOpcode.into()), + InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump.into()), + InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated.into()), + InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow.into()), + InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow.into()), + InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset.into()), + InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision.into()), + InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment.into()), // Check for first call is done separately. + InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError.into()), + InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow.into()), InstructionResult::CreateContractSizeLimit | InstructionResult::CreateContractStartingWithEF => { - Self::Halt(HaltReason::CreateContractSizeLimit) + Self::Halt(HaltReason::CreateContractSizeLimit.into()) } InstructionResult::CreateInitCodeSizeLimit => { - Self::Halt(HaltReason::CreateInitCodeSizeLimit) + Self::Halt(HaltReason::CreateInitCodeSizeLimit.into()) } // TODO (EOF) add proper Revert subtype. InstructionResult::InvalidEOFInitCode => Self::Revert, InstructionResult::FatalExternalError => Self::FatalExternalError, - InstructionResult::EOFOpcodeDisabledInLegacy => Self::Halt(HaltReason::OpcodeNotFound), + InstructionResult::EOFOpcodeDisabledInLegacy => { + Self::Halt(HaltReason::OpcodeNotFound.into()) + } InstructionResult::EOFFunctionStackOverflow => { - Self::Halt(HaltReason::EOFFunctionStackOverflow) + Self::Halt(HaltReason::EOFFunctionStackOverflow.into()) } InstructionResult::ReturnContract => Self::Success(SuccessReason::EofReturnContract), - InstructionResult::EofAuxDataOverflow => Self::Halt(HaltReason::EofAuxDataOverflow), - InstructionResult::EofAuxDataTooSmall => Self::Halt(HaltReason::EofAuxDataTooSmall), + InstructionResult::EofAuxDataOverflow => { + Self::Halt(HaltReason::EofAuxDataOverflow.into()) + } + InstructionResult::EofAuxDataTooSmall => { + Self::Halt(HaltReason::EofAuxDataTooSmall.into()) + } } } } diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index a02058419f..d803b11015 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -125,7 +125,7 @@ pub fn sar(interpreter: &mut Interpreter, _host: & mod tests { use crate::instructions::bitwise::{byte, sar, shl, shr}; use crate::{Contract, DummyHost, Interpreter}; - use revm_primitives::{uint, Env, LatestSpec, U256}; + use revm_primitives::{uint, Env, EthChainSpec, LatestSpec, U256}; #[test] fn test_shift_left() { @@ -202,7 +202,7 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - shl::(&mut interpreter, &mut host); + shl::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); assert_eq!(res, test.expected); } @@ -283,7 +283,7 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - shr::(&mut interpreter, &mut host); + shr::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); assert_eq!(res, test.expected); } @@ -389,7 +389,7 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - sar::(&mut interpreter, &mut host); + sar::, LatestSpec>(&mut interpreter, &mut host); pop!(interpreter, res); assert_eq!(res, test.expected); } @@ -403,7 +403,7 @@ mod tests { expected: U256, } - let mut host = DummyHost::new(Env::default()); + let mut host = DummyHost::new(Env::::default()); let mut interpreter = Interpreter::new(Contract::default(), u64::MAX, false); let input_value = U256::from(0x1234567890abcdef1234567890abcdef_u128); diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 3bf4e43514..4861dda0ec 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -206,7 +206,7 @@ pub fn unknown(interpreter: &mut Interpreter, _host: &mut H) { mod test { use std::sync::Arc; - use revm_primitives::{bytes, eof::TypesSection, Bytecode, Eof, PragueSpec}; + use revm_primitives::{bytes, eof::TypesSection, Bytecode, Eof, EthChainSpec, PragueSpec}; use super::*; use crate::{ @@ -216,7 +216,7 @@ mod test { #[test] fn rjump() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ RJUMP, 0x00, 0x02, STOP, STOP, @@ -230,7 +230,7 @@ mod test { #[test] fn rjumpi() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP, @@ -250,7 +250,7 @@ mod test { #[test] fn rjumpv() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ RJUMPV, @@ -332,7 +332,7 @@ mod test { #[test] fn callf_retf_stop() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01, STOP]); let bytes2 = Bytes::from([RETF]); @@ -363,7 +363,7 @@ mod test { #[test] fn callf_stop() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01]); let bytes2 = Bytes::from([STOP]); @@ -387,7 +387,7 @@ mod test { #[test] fn callf_stack_overflow() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01]); let bytes2 = Bytes::from([STOP]); @@ -404,7 +404,7 @@ mod test { #[test] fn jumpf_stop() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]); let bytes2 = Bytes::from([STOP]); @@ -425,7 +425,7 @@ mod test { #[test] fn jumpf_stack_overflow() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]); let bytes2 = Bytes::from([STOP]); diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index dada108c1d..6fd5dfcf9b 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -83,7 +83,7 @@ pub fn data_copy(interpreter: &mut Interpreter, _host: &mut H) #[cfg(test)] mod test { - use revm_primitives::{b256, bytes, Bytecode, Bytes, Eof, PragueSpec}; + use revm_primitives::{b256, bytes, Bytecode, Bytes, Eof, EthChainSpec, PragueSpec}; use std::sync::Arc; use super::*; @@ -107,7 +107,7 @@ mod test { #[test] fn dataload_dataloadn() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let eof = dummy_eof(Bytes::from([ DATALOAD, DATALOADN, 0x00, 0x00, DATALOAD, DATALOADN, 0x00, 35, DATALOAD, DATALOADN, @@ -163,7 +163,7 @@ mod test { #[test] fn data_copy() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let eof = dummy_eof(Bytes::from([DATACOPY, DATACOPY, DATACOPY, DATACOPY])); diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/host_env.rs index ce934d492f..2ffb25e34c 100644 --- a/crates/interpreter/src/instructions/host_env.rs +++ b/crates/interpreter/src/instructions/host_env.rs @@ -1,6 +1,6 @@ use crate::{ gas, - primitives::{Spec, SpecId::*, U256}, + primitives::{block, Block, Spec, SpecId::*, Transaction, U256}, Host, Interpreter, }; @@ -13,31 +13,31 @@ pub fn chainid(interpreter: &mut Interpreter, host pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().block.coinbase.into_word()); + push_b256!(interpreter, host.env().block.coinbase().into_word()); } pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.timestamp); + push!(interpreter, *host.env().block.timestamp()); } pub fn block_number(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.number); + push!(interpreter, *host.env().block.number()); } pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); if SPEC::enabled(MERGE) { - push_b256!(interpreter, host.env().block.prevrandao.unwrap()); + push_b256!(interpreter, *host.env().block.prevrandao().unwrap()); } else { - push!(interpreter, host.env().block.difficulty); + push!(interpreter, *host.env().block.difficulty()); } } pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.gas_limit); + push!(interpreter, *host.env().block.gas_limit()); } pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { @@ -49,12 +49,12 @@ pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.basefee); + push!(interpreter, *host.env().block.basefee()); } pub fn origin(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().tx.caller.into_word()); + push_b256!(interpreter, host.env().tx.caller().into_word()); } // EIP-4844: Shard Blob Transactions @@ -63,7 +63,7 @@ pub fn blob_hash(interpreter: &mut Interpreter, ho gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, index); let i = as_usize_saturated!(index); - *index = match host.env().tx.blob_hashes.get(i) { + *index = match host.env().tx.blob_hashes().get(i) { Some(hash) => U256::from_be_bytes(hash.0), None => U256::ZERO, }; @@ -75,6 +75,10 @@ pub fn blob_basefee(interpreter: &mut Interpreter, gas!(interpreter, gas::BASE); push!( interpreter, - U256::from(host.env().block.get_blob_gasprice().unwrap_or_default()) + U256::from( + block::get_blob_gasprice(&host.env().block) + .copied() + .unwrap_or_default() + ) ); } diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index d75067e1f2..f39c0fb6c1 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -89,13 +89,13 @@ mod test { use super::*; use crate::{ opcode::{make_instruction_table, DUPN, EXCHANGE, SWAPN}, - primitives::{Bytecode, Bytes, PragueSpec}, + primitives::{Bytecode, Bytes, EthChainSpec, PragueSpec}, DummyHost, Gas, InstructionResult, }; #[test] fn dupn() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ DUPN, 0x00, DUPN, 0x01, DUPN, 0x02, @@ -115,7 +115,7 @@ mod test { #[test] fn swapn() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([SWAPN, 0x00, SWAPN, 0x01]))); @@ -135,7 +135,7 @@ mod test { #[test] fn exchange() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ EXCHANGE, 0x00, EXCHANGE, 0x11, diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 513d334e12..09b80abca6 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -189,13 +189,13 @@ mod test { use super::*; use crate::{ opcode::{make_instruction_table, RETURNDATACOPY, RETURNDATALOAD}, - primitives::{bytes, Bytecode, PragueSpec}, + primitives::{bytes, Bytecode, EthChainSpec, PragueSpec}, DummyHost, Gas, InstructionResult, }; #[test] fn returndataload() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( @@ -254,7 +254,7 @@ mod test { #[test] fn returndatacopy() { let table = make_instruction_table::<_, PragueSpec>(); - let mut host = DummyHost::default(); + let mut host = DummyHost::::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( [ diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 07ab0439d9..971a7b7236 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -462,20 +462,22 @@ pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) mod tests { use super::*; use crate::{opcode::InstructionTable, DummyHost}; - use revm_primitives::CancunSpec; + use revm_primitives::{CancunSpec, EthChainSpec}; #[test] fn object_safety() { let mut interp = Interpreter::new(Contract::default(), u64::MAX, false); - let mut host = crate::DummyHost::default(); - let table: &InstructionTable = - &crate::opcode::make_instruction_table::(); + let mut host = crate::DummyHost::::default(); + let table: &InstructionTable> = + &crate::opcode::make_instruction_table::, CancunSpec>(); let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host); - let host: &mut dyn Host = &mut host as &mut dyn Host; - let table: &InstructionTable = - &crate::opcode::make_instruction_table::(); + let host: &mut dyn Host = + &mut host as &mut dyn Host; + let table: &InstructionTable> = + &crate::opcode::make_instruction_table::, CancunSpec>( + ); let _ = interp.run(EMPTY_SHARED_MEMORY, table, host); } } diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index 49e511f66e..1c1ad3b718 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -1,8 +1,8 @@ -use revm_primitives::TxKind; +use revm_primitives::ChainSpec; use super::analysis::to_analysed; use crate::{ - primitives::{Address, Bytecode, Bytes, Env, B256, U256}, + primitives::{Address, Bytecode, Bytes, Env, Transaction, TxKind, B256, U256}, CallInputs, }; @@ -50,18 +50,22 @@ impl Contract { /// Creates a new contract from the given [`Env`]. #[inline] - pub fn new_env(env: &Env, bytecode: Bytecode, hash: Option) -> Self { - let contract_address = match env.tx.transact_to { + pub fn new_env( + env: &Env, + bytecode: Bytecode, + hash: Option, + ) -> Self { + let contract_address = match env.tx.kind() { TxKind::Call(caller) => caller, TxKind::Create => Address::ZERO, }; Self::new( - env.tx.data.clone(), + env.tx.data().clone(), bytecode, hash, contract_address, - env.tx.caller, - env.tx.value, + *env.tx.caller(), + *env.tx.value(), ) } diff --git a/crates/interpreter/src/interpreter_action/call_inputs.rs b/crates/interpreter/src/interpreter_action/call_inputs.rs index 85d0c8fb71..2e833609ef 100644 --- a/crates/interpreter/src/interpreter_action/call_inputs.rs +++ b/crates/interpreter/src/interpreter_action/call_inputs.rs @@ -1,4 +1,6 @@ -use crate::primitives::{Address, Bytes, TxEnv, TxKind, U256}; +use revm_primitives::Transaction; + +use crate::primitives::{Address, Bytes, TxKind, U256}; use core::ops::Range; use std::boxed::Box; @@ -46,17 +48,17 @@ impl CallInputs { /// Creates new call inputs. /// /// Returns `None` if the transaction is not a call. - pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { - let TxKind::Call(target_address) = tx_env.transact_to else { + pub fn new(tx_env: &impl Transaction, gas_limit: u64) -> Option { + let TxKind::Call(target_address) = tx_env.kind() else { return None; }; Some(CallInputs { - input: tx_env.data.clone(), + input: tx_env.data().clone(), gas_limit, target_address, bytecode_address: target_address, - caller: tx_env.caller, - value: CallValue::Transfer(tx_env.value), + caller: *tx_env.caller(), + value: CallValue::Transfer(*tx_env.value()), scheme: CallScheme::Call, is_static: false, is_eof: false, @@ -67,7 +69,7 @@ impl CallInputs { /// Creates new boxed call inputs. /// /// Returns `None` if the transaction is not a call. - pub fn new_boxed(tx_env: &TxEnv, gas_limit: u64) -> Option> { + pub fn new_boxed(tx_env: &impl Transaction, gas_limit: u64) -> Option> { Self::new(tx_env, gas_limit).map(Box::new) } diff --git a/crates/interpreter/src/interpreter_action/create_inputs.rs b/crates/interpreter/src/interpreter_action/create_inputs.rs index adae96ac2d..151df91d21 100644 --- a/crates/interpreter/src/interpreter_action/create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/create_inputs.rs @@ -1,5 +1,7 @@ +use revm_primitives::Transaction; + pub use crate::primitives::CreateScheme; -use crate::primitives::{Address, Bytes, TxEnv, TxKind, U256}; +use crate::primitives::{Address, Bytes, TxKind, U256}; use std::boxed::Box; /// Inputs for a create call. @@ -20,22 +22,22 @@ pub struct CreateInputs { impl CreateInputs { /// Creates new create inputs. - pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { - let TxKind::Create = tx_env.transact_to else { + pub fn new(tx_env: &impl Transaction, gas_limit: u64) -> Option { + let TxKind::Create = tx_env.kind() else { return None; }; Some(CreateInputs { - caller: tx_env.caller, + caller: *tx_env.caller(), scheme: CreateScheme::Create, - value: tx_env.value, - init_code: tx_env.data.clone(), + value: *tx_env.value(), + init_code: tx_env.data().clone(), gas_limit, }) } /// Returns boxed create inputs. - pub fn new_boxed(tx_env: &TxEnv, gas_limit: u64) -> Option> { + pub fn new_boxed(tx_env: &impl Transaction, gas_limit: u64) -> Option> { Self::new(tx_env, gas_limit).map(Box::new) } diff --git a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs index 30bc525a4e..fd8b63f1dd 100644 --- a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs @@ -1,4 +1,6 @@ -use crate::primitives::{Address, Bytes, Eof, TxEnv, U256}; +use revm_primitives::ChainSpec; + +use crate::primitives::{Address, Bytes, Eof, Transaction as _, U256}; /// EOF create can be called from two places: /// * EOFCREATE opcode @@ -73,13 +75,13 @@ impl EOFCreateInputs { } /// Creates new EOFCreateInputs from transaction. - pub fn new_tx(tx: &TxEnv, gas_limit: u64) -> Self { + pub fn new_tx(tx: &ChainSpecT::Transaction, gas_limit: u64) -> Self { EOFCreateInputs::new( - tx.caller, - tx.value, + *tx.caller(), + *tx.value(), gas_limit, EOFCreateKind::Tx { - initdata: tx.data.clone(), + initdata: tx.data().clone(), }, ) } diff --git a/crates/interpreter/src/opcode.rs b/crates/interpreter/src/opcode.rs index 5617a0d73c..a07f4ae6e7 100644 --- a/crates/interpreter/src/opcode.rs +++ b/crates/interpreter/src/opcode.rs @@ -427,32 +427,32 @@ macro_rules! opcodes { opcodes! { 0x00 => STOP => control::stop => stack_io(0, 0), terminating; - 0x01 => ADD => arithmetic::add => stack_io(2, 1); - 0x02 => MUL => arithmetic::mul => stack_io(2, 1); - 0x03 => SUB => arithmetic::sub => stack_io(2, 1); - 0x04 => DIV => arithmetic::div => stack_io(2, 1); - 0x05 => SDIV => arithmetic::sdiv => stack_io(2, 1); - 0x06 => MOD => arithmetic::rem => stack_io(2, 1); - 0x07 => SMOD => arithmetic::smod => stack_io(2, 1); - 0x08 => ADDMOD => arithmetic::addmod => stack_io(3, 1); - 0x09 => MULMOD => arithmetic::mulmod => stack_io(3, 1); + 0x01 => ADD => arithmetic::add => stack_io(2, 1); + 0x02 => MUL => arithmetic::mul => stack_io(2, 1); + 0x03 => SUB => arithmetic::sub => stack_io(2, 1); + 0x04 => DIV => arithmetic::div => stack_io(2, 1); + 0x05 => SDIV => arithmetic::sdiv => stack_io(2, 1); + 0x06 => MOD => arithmetic::rem => stack_io(2, 1); + 0x07 => SMOD => arithmetic::smod => stack_io(2, 1); + 0x08 => ADDMOD => arithmetic::addmod => stack_io(3, 1); + 0x09 => MULMOD => arithmetic::mulmod => stack_io(3, 1); 0x0A => EXP => arithmetic::exp:: => stack_io(2, 1); - 0x0B => SIGNEXTEND => arithmetic::signextend => stack_io(2, 1); + 0x0B => SIGNEXTEND => arithmetic::signextend => stack_io(2, 1); // 0x0C // 0x0D // 0x0E // 0x0F - 0x10 => LT => bitwise::lt => stack_io(2, 1); - 0x11 => GT => bitwise::gt => stack_io(2, 1); - 0x12 => SLT => bitwise::slt => stack_io(2, 1); - 0x13 => SGT => bitwise::sgt => stack_io(2, 1); - 0x14 => EQ => bitwise::eq => stack_io(2, 1); - 0x15 => ISZERO => bitwise::iszero => stack_io(1, 1); - 0x16 => AND => bitwise::bitand => stack_io(2, 1); - 0x17 => OR => bitwise::bitor => stack_io(2, 1); - 0x18 => XOR => bitwise::bitxor => stack_io(2, 1); - 0x19 => NOT => bitwise::not => stack_io(1, 1); - 0x1A => BYTE => bitwise::byte => stack_io(2, 1); + 0x10 => LT => bitwise::lt => stack_io(2, 1); + 0x11 => GT => bitwise::gt => stack_io(2, 1); + 0x12 => SLT => bitwise::slt => stack_io(2, 1); + 0x13 => SGT => bitwise::sgt => stack_io(2, 1); + 0x14 => EQ => bitwise::eq => stack_io(2, 1); + 0x15 => ISZERO => bitwise::iszero => stack_io(1, 1); + 0x16 => AND => bitwise::bitand => stack_io(2, 1); + 0x17 => OR => bitwise::bitor => stack_io(2, 1); + 0x18 => XOR => bitwise::bitxor => stack_io(2, 1); + 0x19 => NOT => bitwise::not => stack_io(1, 1); + 0x1A => BYTE => bitwise::byte => stack_io(2, 1); 0x1B => SHL => bitwise::shl:: => stack_io(2, 1); 0x1C => SHR => bitwise::shr:: => stack_io(2, 1); 0x1D => SAR => bitwise::sar:: => stack_io(2, 1); @@ -474,24 +474,24 @@ opcodes! { // 0x2D // 0x2E // 0x2F - 0x30 => ADDRESS => system::address => stack_io(0, 1); + 0x30 => ADDRESS => system::address => stack_io(0, 1); 0x31 => BALANCE => host::balance:: => stack_io(1, 1); - 0x32 => ORIGIN => host_env::origin => stack_io(0, 1); - 0x33 => CALLER => system::caller => stack_io(0, 1); - 0x34 => CALLVALUE => system::callvalue => stack_io(0, 1); - 0x35 => CALLDATALOAD => system::calldataload => stack_io(1, 1); - 0x36 => CALLDATASIZE => system::calldatasize => stack_io(0, 1); - 0x37 => CALLDATACOPY => system::calldatacopy => stack_io(3, 0); - 0x38 => CODESIZE => system::codesize => stack_io(0, 1), not_eof; - 0x39 => CODECOPY => system::codecopy => stack_io(3, 0), not_eof; - - 0x3A => GASPRICE => host_env::gasprice => stack_io(0, 1); + 0x32 => ORIGIN => host_env::origin => stack_io(0, 1); + 0x33 => CALLER => system::caller => stack_io(0, 1); + 0x34 => CALLVALUE => system::callvalue => stack_io(0, 1); + 0x35 => CALLDATALOAD => system::calldataload => stack_io(1, 1); + 0x36 => CALLDATASIZE => system::calldatasize => stack_io(0, 1); + 0x37 => CALLDATACOPY => system::calldatacopy => stack_io(3, 0); + 0x38 => CODESIZE => system::codesize => stack_io(0, 1), not_eof; + 0x39 => CODECOPY => system::codecopy => stack_io(3, 0), not_eof; + + 0x3A => GASPRICE => host_env::gasprice => stack_io(0, 1); 0x3B => EXTCODESIZE => host::extcodesize:: => stack_io(1, 1), not_eof; 0x3C => EXTCODECOPY => host::extcodecopy:: => stack_io(4, 0), not_eof; 0x3D => RETURNDATASIZE => system::returndatasize:: => stack_io(0, 1); 0x3E => RETURNDATACOPY => system::returndatacopy:: => stack_io(3, 0); 0x3F => EXTCODEHASH => host::extcodehash:: => stack_io(1, 1), not_eof; - 0x40 => BLOCKHASH => host::blockhash:: => stack_io(1, 1); + 0x40 => BLOCKHASH => host::blockhash:: => stack_io(1, 1); 0x41 => COINBASE => host_env::coinbase => stack_io(0, 1); 0x42 => TIMESTAMP => host_env::timestamp => stack_io(0, 1); 0x43 => NUMBER => host_env::block_number => stack_io(0, 1); @@ -507,10 +507,10 @@ opcodes! { // 0x4D // 0x4E // 0x4F - 0x50 => POP => stack::pop => stack_io(1, 0); - 0x51 => MLOAD => memory::mload => stack_io(1, 1); - 0x52 => MSTORE => memory::mstore => stack_io(2, 0); - 0x53 => MSTORE8 => memory::mstore8 => stack_io(2, 0); + 0x50 => POP => stack::pop => stack_io(1, 0); + 0x51 => MLOAD => memory::mload => stack_io(1, 1); + 0x52 => MSTORE => memory::mstore => stack_io(2, 0); + 0x53 => MSTORE8 => memory::mstore8 => stack_io(2, 0); 0x54 => SLOAD => host::sload:: => stack_io(1, 1); 0x55 => SSTORE => host::sstore:: => stack_io(2, 0); 0x56 => JUMP => control::jump => stack_io(1, 0), not_eof; @@ -674,7 +674,7 @@ opcodes! { 0xF0 => CREATE => contract::create:: => stack_io(3, 1), not_eof; 0xF1 => CALL => contract::call:: => stack_io(7, 1), not_eof; 0xF2 => CALLCODE => contract::call_code:: => stack_io(7, 1), not_eof; - 0xF3 => RETURN => control::ret => stack_io(2, 0), terminating; + 0xF3 => RETURN => control::ret => stack_io(2, 0), terminating; 0xF4 => DELEGATECALL => contract::delegate_call:: => stack_io(6, 1), not_eof; 0xF5 => CREATE2 => contract::create:: => stack_io(4, 1), not_eof; // 0xF6 @@ -685,7 +685,7 @@ opcodes! { 0xFB => EXTSTATICCALL => contract::extstaticcall => stack_io(3, 1); // 0xFC 0xFD => REVERT => control::revert:: => stack_io(2, 0), terminating; - 0xFE => INVALID => control::invalid => stack_io(0, 0), terminating; + 0xFE => INVALID => control::invalid => stack_io(0, 0), terminating; 0xFF => SELFDESTRUCT => host::selfdestruct:: => stack_io(1, 0), not_eof, terminating; } diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index b27ef6f3be..824de57709 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -78,14 +78,6 @@ hashbrown = ["revm-primitives/hashbrown"] asm-keccak = ["revm-primitives/asm-keccak"] optimism = ["revm-primitives/optimism", "secp256r1"] -# Optimism default handler enabled Optimism handler register by default in EvmBuilder. -optimism-default-handler = [ - "optimism", - "revm-primitives/optimism-default-handler", -] -negate-optimism-default-handler = [ - "revm-primitives/negate-optimism-default-handler", -] # Enables the p256verify precompile. secp256r1 = ["dep:p256"] diff --git a/crates/precompile/benches/bench.rs b/crates/precompile/benches/bench.rs index faae103a95..366b6331a2 100644 --- a/crates/precompile/benches/bench.rs +++ b/crates/precompile/benches/bench.rs @@ -9,7 +9,7 @@ use revm_precompile::{ secp256k1::ec_recover_run, Bytes, }; -use revm_primitives::{hex, keccak256, Env, U256, VERSIONED_HASH_VERSION_KZG}; +use revm_primitives::{hex, keccak256, CfgEnv, U256, VERSIONED_HASH_VERSION_KZG}; use secp256k1::{Message, SecretKey, SECP256K1}; use sha2::{Digest, Sha256}; @@ -106,7 +106,7 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { let kzg_input = [versioned_hash, z, y, commitment, proof].concat().into(); let gas = 50000; - let env = Env::default(); + let env = CfgEnv::default(); let output = run(&kzg_input, gas, &env).unwrap(); println!("gas used by kzg precompile: {:?}", output.gas_used); diff --git a/crates/precompile/src/kzg_point_evaluation.rs b/crates/precompile/src/kzg_point_evaluation.rs index 22192ce56d..f7bf2f195e 100644 --- a/crates/precompile/src/kzg_point_evaluation.rs +++ b/crates/precompile/src/kzg_point_evaluation.rs @@ -1,6 +1,6 @@ use crate::{Address, Error, Precompile, PrecompileResult, PrecompileWithAddress}; use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; -use revm_primitives::{hex_literal::hex, Bytes, Env, PrecompileOutput}; +use revm_primitives::{hex_literal::hex, Bytes, CfgEnv, PrecompileOutput}; use sha2::{Digest, Sha256}; pub const POINT_EVALUATION: PrecompileWithAddress = @@ -24,7 +24,7 @@ pub const RETURN_VALUE: &[u8; 64] = &hex!( /// | versioned_hash | z | y | commitment | proof | /// | 32 | 32 | 32 | 48 | 48 | /// with z and y being padded 32 byte big endian values -pub fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult { +pub fn run(input: &Bytes, gas_limit: u64, cfg: &CfgEnv) -> PrecompileResult { if gas_limit < GAS_COST { return Err(Error::OutOfGas.into()); } @@ -46,7 +46,7 @@ pub fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult { let z = as_bytes32(&input[32..64]); let y = as_bytes32(&input[64..96]); let proof = as_bytes48(&input[144..192]); - if !verify_kzg_proof(commitment, z, y, proof, env.cfg.kzg_settings.get()) { + if !verify_kzg_proof(commitment, z, y, proof, cfg.kzg_settings.get()) { return Err(Error::BlobVerifyKzgProofFailed.into()); } @@ -112,7 +112,7 @@ mod tests { let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); let gas = 50000; - let env = Env::default(); + let env = CfgEnv::default(); let output = run(&input.into(), gas, &env).unwrap(); assert_eq!(output.gas_used, gas); assert_eq!(output.bytes[..], expected_output); diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index 1425f3421a..8dc3d4e218 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -283,10 +283,6 @@ impl PrecompileSpecId { CANCUN => Self::CANCUN, PRAGUE | PRAGUE_EOF => Self::PRAGUE, LATEST => Self::LATEST, - #[cfg(feature = "optimism")] - BEDROCK | REGOLITH | CANYON => Self::BERLIN, - #[cfg(feature = "optimism")] - ECOTONE | FJORD => Self::CANCUN, } } } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index a6a0225ac1..dc363140cd 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -22,7 +22,7 @@ rust_2018_idioms = "deny" all = "warn" [dependencies] -alloy-eips = { version = "0.1", default-features = false } +alloy-eips = { version = "0.1.2", default-features = false } alloy-primitives = { version = "0.7.2", default-features = false, features = [ "rlp", ] } @@ -38,6 +38,7 @@ once_cell = { version = "1.19", default-features = false, optional = true } # utility enumn = "0.1" derive_more = { version = "0.99", optional = true } +derive-where = { version = "1.2.7", default-features = false } cfg-if = "1" dyn-clone = "1.0" @@ -81,9 +82,6 @@ asm-keccak = ["alloy-primitives/asm-keccak"] portable = ["c-kzg?/portable"] optimism = [] -# Optimism default handler enabled Optimism handler register by default in EvmBuilder. -optimism-default-handler = ["optimism"] -negate-optimism-default-handler = [] dev = [ "memory_limit", @@ -93,6 +91,7 @@ dev = [ "optional_gas_refund", "optional_no_base_fee", "optional_beneficiary_reward", + "optional_nonce_check", ] memory_limit = [] optional_balance_check = [] @@ -101,6 +100,7 @@ optional_eip3607 = [] optional_gas_refund = [] optional_no_base_fee = [] optional_beneficiary_reward = [] +optional_nonce_check = [] rand = ["alloy-primitives/rand"] # See comments in `revm-precompile` diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs new file mode 100644 index 0000000000..1a5003acb4 --- /dev/null +++ b/crates/primitives/src/block.rs @@ -0,0 +1,66 @@ +use crate::{Address, BlobExcessGasAndPrice, B256, U256}; + +/// Trait for retrieving block information required for execution. +pub trait Block { + /// The number of ancestor blocks of this block (block height). + fn number(&self) -> &U256; + + /// Coinbase or miner or address that created and signed the block. + /// + /// This is the receiver address of all the gas spent in the block. + fn coinbase(&self) -> &Address; + + /// The timestamp of the block in seconds since the UNIX epoch. + fn timestamp(&self) -> &U256; + + /// The gas limit of the block. + fn gas_limit(&self) -> &U256; + + /// The base fee per gas, added in the London upgrade with [EIP-1559]. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + fn basefee(&self) -> &U256; + + /// The difficulty of the block. + /// + /// Unused after the Paris (AKA the merge) upgrade, and replaced by `prevrandao`. + fn difficulty(&self) -> &U256; + + /// The output of the randomness beacon provided by the beacon chain. + /// + /// Replaces `difficulty` after the Paris (AKA the merge) upgrade with [EIP-4399]. + /// + /// NOTE: `prevrandao` can be found in a block in place of `mix_hash`. + /// + /// [EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399 + fn prevrandao(&self) -> Option<&B256>; + + /// Excess blob gas and blob gasprice. + /// See also [`crate::calc_excess_blob_gas`] + /// and [`crate::calc_blob_gasprice`]. + /// + /// Incorporated as part of the Cancun upgrade via [EIP-4844]. + /// + /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 + fn blob_excess_gas_and_price(&self) -> Option<&BlobExcessGasAndPrice>; +} + +/// See [EIP-4844] and [`crate::calc_blob_gasprice`]. +/// +/// Returns `None` if `Cancun` is not enabled. This is enforced in [`crate::Env::validate_block_env`]. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +pub fn get_blob_gasprice(block: &impl Block) -> Option<&u128> { + block.blob_excess_gas_and_price().map(|a| &a.blob_gasprice) +} + +/// Return `blob_excess_gas` header field. See [EIP-4844]. +/// +/// Returns `None` if `Cancun` is not enabled. This is enforced in [`crate::Env::validate_block_env`]. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +pub fn get_blob_excess_gas(block: &impl Block) -> Option { + block.blob_excess_gas_and_price().map(|a| a.excess_blob_gas) +} diff --git a/crates/primitives/src/chain_spec.rs b/crates/primitives/src/chain_spec.rs new file mode 100644 index 0000000000..4b383fb944 --- /dev/null +++ b/crates/primitives/src/chain_spec.rs @@ -0,0 +1,57 @@ +use cfg_if::cfg_if; + +use crate::{Block, SpecId, Transaction}; + +use core::{fmt::Debug, hash::Hash}; + +/// The type that enumerates the chain's hardforks. +pub trait HardforkTrait: Clone + Copy + Default + PartialEq + Eq + Into {} + +impl HardforkTrait for HardforkT where + HardforkT: Clone + Copy + Default + PartialEq + Eq + Into +{ +} + +/// The type that enumerates chain-specific halt reasons. +pub trait HaltReasonTrait: Clone + Debug + PartialEq + Eq + From {} + +impl HaltReasonTrait for HaltReasonT where + HaltReasonT: Clone + Debug + PartialEq + Eq + From +{ +} + +pub trait TransactionValidation { + cfg_if! { + if #[cfg(feature = "std")] { + /// An error that occurs when validating a transaction. + type ValidationError: Debug + std::error::Error; + } else { + /// An error that occurs when validating a transaction. + type ValidationError: Debug; + } + } +} + +pub trait ChainSpec: Sized + 'static { + /// The type that contains all block information. + type Block: Block; + + /// The type that contains all transaction information. + type Transaction: Transaction + TransactionValidation; + + /// The type that enumerates the chain's hardforks. + type Hardfork: HardforkTrait; + + /// Halt reason type. + type HaltReason: HaltReasonTrait; +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct EthChainSpec; + +impl ChainSpec for EthChainSpec { + type Block = crate::BlockEnv; + type Hardfork = SpecId; + type HaltReason = crate::HaltReason; + type Transaction = crate::TxEnv; +} diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 162aa0c130..e5df04e0bb 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -1,50 +1,47 @@ -pub mod handler_cfg; - -use alloy_primitives::TxKind; -pub use handler_cfg::{CfgEnvWithHandlerCfg, EnvWithHandlerCfg, HandlerCfg}; +use derive_where::derive_where; use crate::{ - calc_blob_gasprice, AccessListItem, Account, Address, Bytes, InvalidHeader, InvalidTransaction, - Spec, SpecId, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, - U256, VERSIONED_HASH_VERSION_KZG, + block, calc_blob_gasprice, AccessListItem, Account, Address, Block, Bytes, ChainSpec, + InvalidHeader, InvalidTransaction, Spec, SpecId, TransactionValidation, B256, KECCAK_EMPTY, + MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256, VERSIONED_HASH_VERSION_KZG, +}; +use crate::{ + transaction::{self, Transaction}, + TxKind, }; use core::cmp::{min, Ordering}; +use core::fmt::Debug; use core::hash::Hash; use std::boxed::Box; use std::vec::Vec; /// EVM environment configuration. -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive_where(Clone, Debug, Default; ChainSpecT::Block, ChainSpecT::Transaction)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Env { +pub struct Env { /// Configuration of the EVM itself. pub cfg: CfgEnv, /// Configuration of the block the transaction is in. - pub block: BlockEnv, + pub block: ChainSpecT::Block, /// Configuration of the transaction that is being executed. - pub tx: TxEnv, + pub tx: ChainSpecT::Transaction, } -impl Env { - /// Resets environment to default values. - #[inline] - pub fn clear(&mut self) { - *self = Self::default(); - } - +impl Env { /// Create boxed [Env]. #[inline] - pub fn boxed(cfg: CfgEnv, block: BlockEnv, tx: TxEnv) -> Box { + pub fn boxed(cfg: CfgEnv, block: ChainSpecT::Block, tx: ChainSpecT::Transaction) -> Box { Box::new(Self { cfg, block, tx }) } /// Calculates the effective gas price of the transaction. #[inline] pub fn effective_gas_price(&self) -> U256 { - if let Some(priority_fee) = self.tx.gas_priority_fee { - min(self.tx.gas_price, self.block.basefee + priority_fee) + let gas_price = self.tx.gas_price(); + if let Some(priority_fee) = self.tx.max_priority_fee_per_gas() { + min(*gas_price, self.block.basefee() + priority_fee) } else { - self.tx.gas_price + *gas_price } } @@ -55,8 +52,9 @@ impl Env { /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 #[inline] pub fn calc_data_fee(&self) -> Option { - self.block.get_blob_gasprice().map(|blob_gas_price| { - U256::from(blob_gas_price).saturating_mul(U256::from(self.tx.get_total_blob_gas())) + block::get_blob_gasprice(&self.block).map(|blob_gas_price| { + U256::from(*blob_gas_price) + .saturating_mul(U256::from(transaction::get_total_blob_gas(&self.tx))) }) } @@ -68,8 +66,9 @@ impl Env { /// See EIP-4844: /// pub fn calc_max_data_fee(&self) -> Option { - self.tx.max_fee_per_blob_gas.map(|max_fee_per_blob_gas| { - max_fee_per_blob_gas.saturating_mul(U256::from(self.tx.get_total_blob_gas())) + self.tx.max_fee_per_blob_gas().map(|max_fee_per_blob_gas| { + max_fee_per_blob_gas + .saturating_mul(U256::from(transaction::get_total_blob_gas(&self.tx))) }) } @@ -77,11 +76,11 @@ impl Env { #[inline] pub fn validate_block_env(&self) -> Result<(), InvalidHeader> { // `prevrandao` is required for the merge - if SPEC::enabled(SpecId::MERGE) && self.block.prevrandao.is_none() { + if SPEC::enabled(SpecId::MERGE) && self.block.prevrandao().is_none() { return Err(InvalidHeader::PrevrandaoNotSet); } // `excess_blob_gas` is required for Cancun - if SPEC::enabled(SpecId::CANCUN) && self.block.blob_excess_gas_and_price.is_none() { + if SPEC::enabled(SpecId::CANCUN) && self.block.blob_excess_gas_and_price().is_none() { return Err(InvalidHeader::ExcessBlobGasNotSet); } Ok(()) @@ -94,8 +93,8 @@ impl Env { pub fn validate_tx(&self) -> Result<(), InvalidTransaction> { // BASEFEE tx check if SPEC::enabled(SpecId::LONDON) { - if let Some(priority_fee) = self.tx.gas_priority_fee { - if priority_fee > self.tx.gas_price { + if let Some(priority_fee) = self.tx.max_priority_fee_per_gas() { + if priority_fee > self.tx.gas_price() { // or gas_max_fee for eip1559 return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee); } @@ -103,7 +102,7 @@ impl Env { // check minimal cost against basefee if !self.cfg.is_base_fee_check_disabled() - && self.effective_gas_price() < self.block.basefee + && self.effective_gas_price() < *self.block.basefee() { return Err(InvalidTransaction::GasPriceLessThanBasefee); } @@ -111,32 +110,32 @@ impl Env { // Check if gas_limit is more than block_gas_limit if !self.cfg.is_block_gas_limit_disabled() - && U256::from(self.tx.gas_limit) > self.block.gas_limit + && U256::from(self.tx.gas_limit()) > *self.block.gas_limit() { return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); } // EIP-3860: Limit and meter initcode - if SPEC::enabled(SpecId::SHANGHAI) && self.tx.transact_to.is_create() { + if SPEC::enabled(SpecId::SHANGHAI) && self.tx.kind().is_create() { let max_initcode_size = self .cfg .limit_contract_code_size .map(|limit| limit.saturating_mul(2)) .unwrap_or(MAX_INITCODE_SIZE); - if self.tx.data.len() > max_initcode_size { + if self.tx.data().len() > max_initcode_size { return Err(InvalidTransaction::CreateInitCodeSizeLimit); } } // Check if the transaction's chain id is correct - if let Some(tx_chain_id) = self.tx.chain_id { + if let Some(tx_chain_id) = self.tx.chain_id() { if tx_chain_id != self.cfg.chain_id { return Err(InvalidTransaction::InvalidChainId); } } // Check that access list is empty for transactions before BERLIN - if !SPEC::enabled(SpecId::BERLIN) && !self.tx.access_list.is_empty() { + if !SPEC::enabled(SpecId::BERLIN) && !self.tx.access_list().is_empty() { return Err(InvalidTransaction::AccessListNotSupported); } @@ -144,15 +143,15 @@ impl Env { // - For before CANCUN, check that `blob_hashes` and `max_fee_per_blob_gas` are empty / not set if SPEC::enabled(SpecId::CANCUN) { // Presence of max_fee_per_blob_gas means that this is blob transaction. - if let Some(max) = self.tx.max_fee_per_blob_gas { + if let Some(max) = self.tx.max_fee_per_blob_gas() { // ensure that the user was willing to at least pay the current blob gasprice - let price = self.block.get_blob_gasprice().expect("already checked"); - if U256::from(price) > max { + let price = block::get_blob_gasprice(&self.block).expect("already checked"); + if U256::from(*price) > *max { return Err(InvalidTransaction::BlobGasPriceGreaterThanMax); } // there must be at least one blob - if self.tx.blob_hashes.is_empty() { + if self.tx.blob_hashes().is_empty() { return Err(InvalidTransaction::EmptyBlobs); } @@ -160,12 +159,12 @@ impl Env { // that it MUST NOT be nil and therefore must always represent // a 20-byte address. This means that blob transactions cannot // have the form of a create transaction. - if self.tx.transact_to.is_create() { + if self.tx.kind().is_create() { return Err(InvalidTransaction::BlobCreateTransaction); } // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG - for blob in self.tx.blob_hashes.iter() { + for blob in self.tx.blob_hashes() { if blob[0] != VERSIONED_HASH_VERSION_KZG { return Err(InvalidTransaction::BlobVersionNotSupported); } @@ -173,7 +172,7 @@ impl Env { // ensure the total blob gas spent is at most equal to the limit // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK - let num_blobs = self.tx.blob_hashes.len(); + let num_blobs = self.tx.blob_hashes().len(); if num_blobs > MAX_BLOB_NUMBER_PER_BLOCK as usize { return Err(InvalidTransaction::TooManyBlobs { have: num_blobs, @@ -182,10 +181,10 @@ impl Env { } } } else { - if !self.tx.blob_hashes.is_empty() { + if !self.tx.blob_hashes().is_empty() { return Err(InvalidTransaction::BlobVersionedHashesNotSupported); } - if self.tx.max_fee_per_blob_gas.is_some() { + if self.tx.max_fee_per_blob_gas().is_some() { return Err(InvalidTransaction::MaxFeePerBlobGasNotSupported); } } @@ -207,7 +206,8 @@ impl Env { } // Check that the transaction's nonce is correct - if let Some(tx) = self.tx.nonce { + if !self.cfg.is_nonce_check_disabled() { + let tx = self.tx.nonce(); let state = account.info.nonce; match tx.cmp(&state) { Ordering::Greater => { @@ -220,9 +220,9 @@ impl Env { } } - let mut balance_check = U256::from(self.tx.gas_limit) - .checked_mul(self.tx.gas_price) - .and_then(|gas_cost| gas_cost.checked_add(self.tx.value)) + let mut balance_check = U256::from(self.tx.gas_limit()) + .checked_mul(*self.tx.gas_price()) + .and_then(|gas_cost| gas_cost.checked_add(*self.tx.value())) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; if SPEC::enabled(SpecId::CANCUN) { @@ -251,6 +251,14 @@ impl Env { } } +impl> Env { + /// Resets environment to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } +} + /// EVM configuration. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Eq, PartialEq)] @@ -305,6 +313,11 @@ pub struct CfgEnv { /// By default, it is set to `false`. #[cfg(feature = "optional_beneficiary_reward")] pub disable_beneficiary_reward: bool, + /// Skips the nonce validation against the account's nonce: + /// [`crate::InvalidTransaction::NonceTooHigh`] and + /// [`crate::InvalidTransaction::NonceTooLow`] + #[cfg(feature = "optional_nonce_check")] + pub disable_nonce_check: bool, } impl CfgEnv { @@ -372,6 +385,16 @@ impl CfgEnv { pub fn is_beneficiary_reward_disabled(&self) -> bool { false } + + #[cfg(feature = "optional_nonce_check")] + pub const fn is_nonce_check_disabled(&self) -> bool { + self.disable_nonce_check + } + + #[cfg(not(feature = "optional_nonce_check"))] + pub const fn is_nonce_check_disabled(&self) -> bool { + false + } } impl Default for CfgEnv { @@ -396,6 +419,8 @@ impl Default for CfgEnv { disable_base_fee: false, #[cfg(feature = "optional_beneficiary_reward")] disable_beneficiary_reward: false, + #[cfg(feature = "optional_nonce_check")] + disable_nonce_check: false, } } } @@ -447,34 +472,47 @@ impl BlockEnv { pub fn set_blob_excess_gas_and_price(&mut self, excess_blob_gas: u64) { self.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(excess_blob_gas)); } - /// See [EIP-4844] and [`crate::calc_blob_gasprice`]. - /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +} + +impl Block for BlockEnv { #[inline] - pub fn get_blob_gasprice(&self) -> Option { - self.blob_excess_gas_and_price - .as_ref() - .map(|a| a.blob_gasprice) + fn number(&self) -> &U256 { + &self.number } - /// Return `blob_excess_gas` header field. See [EIP-4844]. - /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 #[inline] - pub fn get_blob_excess_gas(&self) -> Option { - self.blob_excess_gas_and_price - .as_ref() - .map(|a| a.excess_blob_gas) + fn coinbase(&self) -> &Address { + &self.coinbase } - /// Clears environment and resets fields to default values. #[inline] - pub fn clear(&mut self) { - *self = Self::default(); + fn timestamp(&self) -> &U256 { + &self.timestamp + } + + #[inline] + fn gas_limit(&self) -> &U256 { + &self.gas_limit + } + + #[inline] + fn basefee(&self) -> &U256 { + &self.basefee + } + + #[inline] + fn difficulty(&self) -> &U256 { + &self.difficulty + } + + #[inline] + fn prevrandao(&self) -> Option<&B256> { + self.prevrandao.as_ref() + } + + #[inline] + fn blob_excess_gas_and_price(&self) -> Option<&BlobExcessGasAndPrice> { + self.blob_excess_gas_and_price.as_ref() } } @@ -510,9 +548,7 @@ pub struct TxEnv { /// The data of the transaction. pub data: Bytes, /// The nonce of the transaction. - /// - /// Caution: If set to `None`, then nonce validation against the account's nonce is skipped: [InvalidTransaction::NonceTooHigh] and [InvalidTransaction::NonceTooLow] - pub nonce: Option, + pub nonce: u64, /// The chain ID of the transaction. If set to `None`, no checks are performed. /// @@ -549,34 +585,79 @@ pub struct TxEnv { /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 pub max_fee_per_blob_gas: Option, - - #[cfg_attr(feature = "serde", serde(flatten))] - #[cfg(feature = "optimism")] - /// Optimism fields. - pub optimism: OptimismFields, } -pub enum TxType { - Legacy, - Eip1559, - BlobTx, - EofCreate, -} +impl Transaction for TxEnv { + #[inline] + fn caller(&self) -> &Address { + &self.caller + } -impl TxEnv { - /// See [EIP-4844], [`Env::calc_data_fee`], and [`Env::calc_max_data_fee`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 #[inline] - pub fn get_total_blob_gas(&self) -> u64 { - GAS_PER_BLOB * self.blob_hashes.len() as u64 + fn gas_limit(&self) -> u64 { + self.gas_limit } - /// Clears environment and resets fields to default values. #[inline] - pub fn clear(&mut self) { - *self = Self::default(); + fn gas_price(&self) -> &U256 { + &self.gas_price + } + + #[inline] + fn kind(&self) -> TxKind { + self.transact_to + } + + #[inline] + fn value(&self) -> &U256 { + &self.value + } + + #[inline] + fn data(&self) -> &Bytes { + &self.data + } + + #[inline] + fn nonce(&self) -> u64 { + self.nonce + } + + #[inline] + fn chain_id(&self) -> Option { + self.chain_id + } + + #[inline] + fn access_list(&self) -> &[AccessListItem] { + &self.access_list } + + #[inline] + fn max_priority_fee_per_gas(&self) -> Option<&U256> { + self.gas_priority_fee.as_ref() + } + + #[inline] + fn blob_hashes(&self) -> &[B256] { + &self.blob_hashes + } + + #[inline] + fn max_fee_per_blob_gas(&self) -> Option<&U256> { + self.max_fee_per_blob_gas.as_ref() + } +} + +impl TransactionValidation for TxEnv { + type ValidationError = InvalidTransaction; +} + +pub enum TxType { + Legacy, + Eip1559, + BlobTx, + EofCreate, } impl Default for TxEnv { @@ -590,12 +671,10 @@ impl Default for TxEnv { value: U256::ZERO, data: Bytes::new(), chain_id: None, - nonce: None, + nonce: 0, access_list: Vec::new(), blob_hashes: Vec::new(), max_fee_per_blob_gas: None, - #[cfg(feature = "optimism")] - optimism: OptimismFields::default(), } } } @@ -625,40 +704,6 @@ impl BlobExcessGasAndPrice { } } -/// Additional [TxEnv] fields for optimism. -#[cfg(feature = "optimism")] -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct OptimismFields { - /// The source hash is used to make sure that deposit transactions do - /// not have identical hashes. - /// - /// L1 originated deposit transaction source hashes are computed using - /// the hash of the l1 block hash and the l1 log index. - /// L1 attributes deposit source hashes are computed with the l1 block - /// hash and the sequence number = l2 block number - l2 epoch start - /// block number. - /// - /// These two deposit transaction sources specify a domain in the outer - /// hash so there are no collisions. - pub source_hash: Option, - /// The amount to increase the balance of the `from` account as part of - /// a deposit transaction. This is unconditional and is applied to the - /// `from` account even if the deposit transaction fails since - /// the deposit is pre-paid on L1. - pub mint: Option, - /// Whether or not the transaction is a system transaction. - pub is_system_transaction: Option, - /// An enveloped EIP-2718 typed transaction. This is used - /// to compute the L1 tx cost using the L1 block info, as - /// opposed to requiring downstream apps to compute the cost - /// externally. - /// This field is optional to allow the [TxEnv] to be constructed - /// for non-optimism chains when the `optimism` feature is enabled, - /// but the [CfgEnv] `optimism` field is set to false. - pub enveloped_tx: Option, -} - /// Transaction destination pub type TransactTo = TxKind; @@ -688,11 +733,13 @@ pub enum AnalysisKind { #[cfg(test)] mod tests { + use crate::EthChainSpec; + use super::*; #[test] fn test_validate_tx_chain_id() { - let mut env = Env::default(); + let mut env = Env::::default(); env.tx.chain_id = Some(1); env.cfg.chain_id = 2; assert_eq!( @@ -703,7 +750,7 @@ mod tests { #[test] fn test_validate_tx_access_list() { - let mut env = Env::default(); + let mut env = Env::::default(); env.tx.access_list = vec![AccessListItem { address: Address::ZERO, storage_keys: vec![], diff --git a/crates/primitives/src/env/handler_cfg.rs b/crates/primitives/src/env/handler_cfg.rs deleted file mode 100644 index f57a3e8c99..0000000000 --- a/crates/primitives/src/env/handler_cfg.rs +++ /dev/null @@ -1,159 +0,0 @@ -use super::{BlockEnv, CfgEnv, Env, SpecId, TxEnv}; -use core::ops::{Deref, DerefMut}; -use std::boxed::Box; - -/// Handler configuration fields. It is used to configure the handler. -/// It contains specification id and the Optimism related field if -/// optimism feature is enabled. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct HandlerCfg { - /// Specification identification. - pub spec_id: SpecId, - /// Optimism related field, it will append the Optimism handle register to the EVM. - #[cfg(feature = "optimism")] - pub is_optimism: bool, -} - -impl Default for HandlerCfg { - fn default() -> Self { - Self::new(SpecId::default()) - } -} - -impl HandlerCfg { - /// Creates new `HandlerCfg` instance. - pub fn new(spec_id: SpecId) -> Self { - cfg_if::cfg_if! { - if #[cfg(all(feature = "optimism-default-handler", - not(feature = "negate-optimism-default-handler")))] { - let is_optimism = true; - } else if #[cfg(feature = "optimism")] { - let is_optimism = false; - } - } - Self { - spec_id, - #[cfg(feature = "optimism")] - is_optimism, - } - } - - /// Creates new `HandlerCfg` instance with the optimism feature. - #[cfg(feature = "optimism")] - pub fn new_with_optimism(spec_id: SpecId, is_optimism: bool) -> Self { - Self { - spec_id, - is_optimism, - } - } - - /// Returns `true` if the optimism feature is enabled and flag is set to `true`. - pub fn is_optimism(&self) -> bool { - cfg_if::cfg_if! { - if #[cfg(feature = "optimism")] { - self.is_optimism - } else { - false - } - } - } -} - -/// Configuration environment with the chain spec id. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct CfgEnvWithHandlerCfg { - /// Configuration environment. - pub cfg_env: CfgEnv, - /// Handler configuration fields. - pub handler_cfg: HandlerCfg, -} - -impl CfgEnvWithHandlerCfg { - /// Returns new instance of `CfgEnvWithHandlerCfg` with the handler configuration. - pub fn new(cfg_env: CfgEnv, handler_cfg: HandlerCfg) -> Self { - Self { - cfg_env, - handler_cfg, - } - } - - /// Returns new `CfgEnvWithHandlerCfg` instance with the chain spec id. - /// - /// is_optimism will be set to default value depending on `optimism-default-handler` feature. - pub fn new_with_spec_id(cfg_env: CfgEnv, spec_id: SpecId) -> Self { - Self::new(cfg_env, HandlerCfg::new(spec_id)) - } - - /// Enables the optimism feature. - #[cfg(feature = "optimism")] - pub fn enable_optimism(&mut self) { - self.handler_cfg.is_optimism = true; - } -} - -impl DerefMut for CfgEnvWithHandlerCfg { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cfg_env - } -} - -impl Deref for CfgEnvWithHandlerCfg { - type Target = CfgEnv; - - fn deref(&self) -> &Self::Target { - &self.cfg_env - } -} - -/// Evm environment with the chain spec id. -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct EnvWithHandlerCfg { - /// Evm enironment. - pub env: Box, - /// Handler configuration fields. - pub handler_cfg: HandlerCfg, -} - -impl EnvWithHandlerCfg { - /// Returns new `EnvWithHandlerCfg` instance. - pub fn new(env: Box, handler_cfg: HandlerCfg) -> Self { - Self { env, handler_cfg } - } - - /// Returns new `EnvWithHandlerCfg` instance with the chain spec id. - /// - /// is_optimism will be set to default value depending on `optimism-default-handler` feature. - pub fn new_with_spec_id(env: Box, spec_id: SpecId) -> Self { - Self::new(env, HandlerCfg::new(spec_id)) - } - - /// Takes `CfgEnvWithHandlerCfg` and returns new `EnvWithHandlerCfg` instance. - pub fn new_with_cfg_env(cfg: CfgEnvWithHandlerCfg, block: BlockEnv, tx: TxEnv) -> Self { - Self::new(Env::boxed(cfg.cfg_env, block, tx), cfg.handler_cfg) - } - - /// Returns the specification id. - pub const fn spec_id(&self) -> SpecId { - self.handler_cfg.spec_id - } - - /// Enables the optimism handle register. - #[cfg(feature = "optimism")] - pub fn enable_optimism(&mut self) { - self.handler_cfg.is_optimism = true; - } -} - -impl DerefMut for EnvWithHandlerCfg { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.env - } -} - -impl Deref for EnvWithHandlerCfg { - type Target = Env; - - fn deref(&self) -> &Self::Target { - &self.env - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 78e939d5f7..9c60ab0636 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -12,12 +12,15 @@ mod constants; pub mod db; pub mod env; +pub mod block; +mod chain_spec; #[cfg(feature = "c-kzg")] pub mod kzg; pub mod precompile; pub mod result; pub mod specification; pub mod state; +pub mod transaction; pub mod utilities; pub use alloy_eips::eip2930::{AccessList, AccessListItem}; pub use alloy_primitives::{ @@ -26,6 +29,7 @@ pub use alloy_primitives::{ }; pub use bitvec; pub use bytecode::*; +pub use chain_spec::*; pub use constants::*; pub use env::*; @@ -38,10 +42,12 @@ cfg_if::cfg_if! { } } +pub use block::Block; #[cfg(feature = "c-kzg")] pub use kzg::{EnvKzgSettings, KzgSettings}; pub use precompile::*; pub use result::*; pub use specification::*; pub use state::*; +pub use transaction::Transaction; pub use utilities::*; diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index 58f7cfe953..22b98f3514 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -1,5 +1,5 @@ -use crate::{Bytes, Env}; -use core::fmt::{self}; +use crate::{Bytes, CfgEnv}; +use core::fmt; use dyn_clone::DynClone; use std::{boxed::Box, string::String, sync::Arc}; @@ -25,18 +25,18 @@ impl PrecompileOutput { } pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; -pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult; +pub type EnvPrecompileFn = fn(&Bytes, u64, env: &CfgEnv) -> PrecompileResult; /// Stateful precompile trait. It is used to create /// a arc precompile Precompile::Stateful. pub trait StatefulPrecompile: Sync + Send { - fn call(&self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult; + fn call(&self, bytes: &Bytes, gas_price: u64, env: &CfgEnv) -> PrecompileResult; } /// Mutable stateful precompile trait. It is used to create /// a boxed precompile in Precompile::StatefulMut. pub trait StatefulPrecompileMut: DynClone + Send + Sync { - fn call_mut(&mut self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult; + fn call_mut(&mut self, bytes: &Bytes, gas_price: u64, env: &CfgEnv) -> PrecompileResult; } dyn_clone::clone_trait_object!(StatefulPrecompileMut); @@ -52,13 +52,13 @@ pub type StatefulPrecompileBox = Box; pub enum Precompile { /// Standard simple precompile that takes input and gas limit. Standard(StandardPrecompileFn), - /// Similar to Standard but takes reference to environment. + /// Similar to Standard but takes reference to [`CfgEnv`]. Env(EnvPrecompileFn), /// Stateful precompile that is Arc over [`StatefulPrecompile`] trait. - /// It takes a reference to input, gas limit and environment. + /// It takes a reference to input, gas limit and [`CfgEnv`]. Stateful(StatefulPrecompileArc), /// Mutable stateful precompile that is Box over [`StatefulPrecompileMut`] trait. - /// It takes a reference to input, gas limit and environment. + /// It takes a reference to input, gas limit and [`CfgEnv`]. StatefulMut(StatefulPrecompileBox), } @@ -109,7 +109,7 @@ impl Precompile { } /// Call the precompile with the given input and gas limit and return the result. - pub fn call(&mut self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult { + pub fn call(&mut self, bytes: &Bytes, gas_price: u64, env: &CfgEnv) -> PrecompileResult { match *self { Precompile::Standard(p) => p(bytes, gas_price), Precompile::Env(p) => p(bytes, gas_price, env), @@ -121,7 +121,7 @@ impl Precompile { /// Call the precompile with the given input and gas limit and return the result. /// /// Returns an error if the precompile is mutable. - pub fn call_ref(&self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult { + pub fn call_ref(&self, bytes: &Bytes, gas_price: u64, env: &CfgEnv) -> PrecompileResult { match *self { Precompile::Standard(p) => p(bytes, gas_price), Precompile::Env(p) => p(bytes, gas_price, env), @@ -234,7 +234,7 @@ mod test { &mut self, _bytes: &Bytes, _gas_price: u64, - _env: &Env, + _env: &CfgEnv, ) -> PrecompileResult { Err(PrecompileError::OutOfGas.into()) } @@ -243,7 +243,7 @@ mod test { let mut p = Precompile::new_stateful_mut(MyPrecompile::default()); match &mut p { Precompile::StatefulMut(p) => { - let _ = p.call_mut(&Bytes::new(), 0, &Env::default()); + let _ = p.call_mut(&Bytes::new(), 0, &CfgEnv::default()); } _ => panic!("not a state"), } diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index c7a4469e71..d19e46f8ea 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -1,26 +1,35 @@ -use crate::{Address, Bytes, EvmState, Log, U256}; -use core::fmt; +use derive_where::derive_where; + +use crate::{Address, Bytes, ChainSpec, EvmState, Log, TransactionValidation, U256}; +use core::fmt::{self, Debug}; use std::{boxed::Box, string::String, vec::Vec}; /// Result of EVM execution. -pub type EVMResult = EVMResultGeneric; +pub type EVMResult = + EVMResultGeneric, ChainSpecT, DBError>; /// Generic result of EVM execution. Used to represent error and generic output. -pub type EVMResultGeneric = core::result::Result>; +pub type EVMResultGeneric = + core::result::Result>; + +/// EVM error type for a specific chain. +pub type EVMErrorForChain = EVMError< + DBError, + <::Transaction as TransactionValidation>::ValidationError, +>; #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ResultAndState { +pub struct ResultAndState { /// Status of execution - pub result: ExecutionResult, + pub result: ExecutionResult, /// State that got updated pub state: EvmState, } /// Result of a transaction execution. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ExecutionResult { +#[derive(Debug, PartialEq, Eq, Hash)] +#[derive_where(Clone; ChainSpecT::HaltReason)] +pub enum ExecutionResult { /// Returned successfully Success { reason: SuccessReason, @@ -33,13 +42,13 @@ pub enum ExecutionResult { Revert { gas_used: u64, output: Bytes }, /// Reverted for various reasons and spend all gas. Halt { - reason: HaltReason, + reason: ChainSpecT::HaltReason, /// Halting will spend all the gas, and will be equal to gas_limit. gas_used: u64, }, } -impl ExecutionResult { +impl ExecutionResult { /// Returns if transaction execution is successful. /// 1 indicates success, 0 indicates revert. /// @@ -135,11 +144,10 @@ impl Output { } /// Main EVM error. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum EVMError { +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EVMError { /// Transaction validation error. - Transaction(InvalidTransaction), + Transaction(TransactionValidationErrorT), /// Header validation error. Header(InvalidHeader), /// Database error. @@ -152,9 +160,9 @@ pub enum EVMError { Precompile(String), } -impl EVMError { +impl EVMError { /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged. - pub fn map_db_err(self, op: F) -> EVMError + pub fn map_db_err(self, op: F) -> EVMError where F: FnOnce(DBError) -> E, { @@ -169,7 +177,12 @@ impl EVMError { } #[cfg(feature = "std")] -impl std::error::Error for EVMError { +impl std::error::Error + for EVMError +where + DBError: std::error::Error + 'static, + TransactionValidationErrorT: std::error::Error + 'static, +{ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::Transaction(e) => Some(e), @@ -180,7 +193,12 @@ impl std::error::Error for EVMError fmt::Display for EVMError { +impl fmt::Display + for EVMError +where + DBError: fmt::Display, + TransactionValidationErrorT: fmt::Display, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Transaction(e) => write!(f, "transaction validation error: {e}"), @@ -191,13 +209,15 @@ impl fmt::Display for EVMError { } } -impl From for EVMError { +impl From for EVMError { fn from(value: InvalidTransaction) -> Self { Self::Transaction(value) } } -impl From for EVMError { +impl From + for EVMError +{ fn from(value: InvalidHeader) -> Self { Self::Header(value) } @@ -268,38 +288,6 @@ pub enum InvalidTransaction { BlobVersionNotSupported, /// EOF crate should have `to` address EofCrateShouldHaveToAddress, - /// System transactions are not supported post-regolith hardfork. - /// - /// Before the Regolith hardfork, there was a special field in the `Deposit` transaction - /// type that differentiated between `system` and `user` deposit transactions. This field - /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction - /// is found with this field set to `true` after the hardfork activation. - /// - /// In addition, this error is internal, and bubbles up into a [HaltReason::FailedDeposit] error - /// in the `revm` handler for the consumer to easily handle. This is due to a state transition - /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction - /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and - /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this - /// case for failed deposit transactions. - #[cfg(feature = "optimism")] - DepositSystemTxPostRegolith, - /// Deposit transaction haults bubble up to the global main return handler, wiping state and - /// only increasing the nonce + persisting the mint value. - /// - /// This is a catch-all error for any deposit transaction that is results in a [HaltReason] error - /// post-regolith hardfork. This allows for a consumer to easily handle special cases where - /// a deposit transaction fails during validation, but must still be included in the block. - /// - /// In addition, this error is internal, and bubbles up into a [HaltReason::FailedDeposit] error - /// in the `revm` handler for the consumer to easily handle. This is due to a state transition - /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction - /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and - /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this - /// case for failed deposit transactions. - #[cfg(feature = "optimism")] - HaltedDepositPostRegolith, } #[cfg(feature = "std")] @@ -359,20 +347,6 @@ impl fmt::Display for InvalidTransaction { } Self::BlobVersionNotSupported => write!(f, "blob version not supported"), Self::EofCrateShouldHaveToAddress => write!(f, "EOF crate should have `to` address"), - #[cfg(feature = "optimism")] - Self::DepositSystemTxPostRegolith => { - write!( - f, - "deposit system transactions post regolith hardfork are not supported" - ) - } - #[cfg(feature = "optimism")] - Self::HaltedDepositPostRegolith => { - write!( - f, - "deposit transaction halted post-regolith; error will be bubbled up to main return handler" - ) - } } } } @@ -445,10 +419,6 @@ pub enum HaltReason { EofAuxDataTooSmall, /// EOF Subroutine stack overflow EOFFunctionStackOverflow, - - /* Optimism errors */ - #[cfg(feature = "optimism")] - FailedDeposit, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 51d18010be..8c29c7a6ee 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -5,7 +5,6 @@ pub use SpecId::*; /// Specification IDs and their activation block. /// /// Information was obtained from the [Ethereum Execution Specifications](https://github.com/ethereum/execution-specs) -#[cfg(not(feature = "optimism"))] #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -34,43 +33,6 @@ pub enum SpecId { LATEST = u8::MAX, } -/// Specification IDs and their activation block. -/// -/// Information was obtained from the [Ethereum Execution Specifications](https://github.com/ethereum/execution-specs) -#[cfg(feature = "optimism")] -#[repr(u8)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum SpecId { - FRONTIER = 0, - FRONTIER_THAWING = 1, - HOMESTEAD = 2, - DAO_FORK = 3, - TANGERINE = 4, - SPURIOUS_DRAGON = 5, - BYZANTIUM = 6, - CONSTANTINOPLE = 7, - PETERSBURG = 8, - ISTANBUL = 9, - MUIR_GLACIER = 10, - BERLIN = 11, - LONDON = 12, - ARROW_GLACIER = 13, - GRAY_GLACIER = 14, - MERGE = 15, - BEDROCK = 16, - REGOLITH = 17, - SHANGHAI = 18, - CANYON = 19, - CANCUN = 20, - ECOTONE = 21, - FJORD = 22, - PRAGUE = 23, - PRAGUE_EOF = 24, - #[default] - LATEST = u8::MAX, -} - impl SpecId { /// Returns the `SpecId` for the given `u8`. #[inline] @@ -110,16 +72,6 @@ impl From<&str> for SpecId { "Cancun" => Self::CANCUN, "Prague" => Self::PRAGUE, "PragueEOF" => Self::PRAGUE_EOF, - #[cfg(feature = "optimism")] - "Bedrock" => SpecId::BEDROCK, - #[cfg(feature = "optimism")] - "Regolith" => SpecId::REGOLITH, - #[cfg(feature = "optimism")] - "Canyon" => SpecId::CANYON, - #[cfg(feature = "optimism")] - "Ecotone" => SpecId::ECOTONE, - #[cfg(feature = "optimism")] - "Fjord" => SpecId::FJORD, _ => Self::LATEST, } } @@ -148,16 +100,6 @@ impl From for &'static str { SpecId::CANCUN => "Cancun", SpecId::PRAGUE => "Prague", SpecId::PRAGUE_EOF => "PragueEOF", - #[cfg(feature = "optimism")] - SpecId::BEDROCK => "Bedrock", - #[cfg(feature = "optimism")] - SpecId::REGOLITH => "Regolith", - #[cfg(feature = "optimism")] - SpecId::CANYON => "Canyon", - #[cfg(feature = "optimism")] - SpecId::ECOTONE => "Ecotone", - #[cfg(feature = "optimism")] - SpecId::FJORD => "Fjord", SpecId::LATEST => "Latest", } } @@ -208,99 +150,15 @@ spec!(PRAGUE_EOF, PragueEofSpec); spec!(LATEST, LatestSpec); -// Optimism Hardforks -#[cfg(feature = "optimism")] -spec!(BEDROCK, BedrockSpec); -#[cfg(feature = "optimism")] -spec!(REGOLITH, RegolithSpec); -#[cfg(feature = "optimism")] -spec!(CANYON, CanyonSpec); -#[cfg(feature = "optimism")] -spec!(ECOTONE, EcotoneSpec); -#[cfg(feature = "optimism")] -spec!(FJORD, FjordSpec); - -#[cfg(not(feature = "optimism"))] -#[macro_export] -macro_rules! spec_to_generic { - ($spec_id:expr, $e:expr) => {{ - match $spec_id { - $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { - use $crate::FrontierSpec as SPEC; - $e - } - $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { - use $crate::HomesteadSpec as SPEC; - $e - } - $crate::SpecId::TANGERINE => { - use $crate::TangerineSpec as SPEC; - $e - } - $crate::SpecId::SPURIOUS_DRAGON => { - use $crate::SpuriousDragonSpec as SPEC; - $e - } - $crate::SpecId::BYZANTIUM => { - use $crate::ByzantiumSpec as SPEC; - $e - } - $crate::SpecId::PETERSBURG | $crate::SpecId::CONSTANTINOPLE => { - use $crate::PetersburgSpec as SPEC; - $e - } - $crate::SpecId::ISTANBUL | $crate::SpecId::MUIR_GLACIER => { - use $crate::IstanbulSpec as SPEC; - $e - } - $crate::SpecId::BERLIN => { - use $crate::BerlinSpec as SPEC; - $e - } - $crate::SpecId::LONDON - | $crate::SpecId::ARROW_GLACIER - | $crate::SpecId::GRAY_GLACIER => { - use $crate::LondonSpec as SPEC; - $e - } - $crate::SpecId::MERGE => { - use $crate::MergeSpec as SPEC; - $e - } - $crate::SpecId::SHANGHAI => { - use $crate::ShanghaiSpec as SPEC; - $e - } - $crate::SpecId::CANCUN => { - use $crate::CancunSpec as SPEC; - $e - } - $crate::SpecId::LATEST => { - use $crate::LatestSpec as SPEC; - $e - } - $crate::SpecId::PRAGUE => { - use $crate::PragueSpec as SPEC; - $e - } - $crate::SpecId::PRAGUE_EOF => { - use $crate::PragueEofSpec as SPEC; - $e - } - } - }}; -} - -#[cfg(feature = "optimism")] #[macro_export] macro_rules! spec_to_generic { ($spec_id:expr, $e:expr) => {{ match $spec_id { - $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + $crate::SpecId::FRONTIER | $crate::SpecId::FRONTIER_THAWING => { use $crate::FrontierSpec as SPEC; $e } - $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { + $crate::SpecId::HOMESTEAD | $crate::SpecId::DAO_FORK => { use $crate::HomesteadSpec as SPEC; $e } @@ -358,26 +216,6 @@ macro_rules! spec_to_generic { use $crate::PragueEofSpec as SPEC; $e } - $crate::SpecId::BEDROCK => { - use $crate::BedrockSpec as SPEC; - $e - } - $crate::SpecId::REGOLITH => { - use $crate::RegolithSpec as SPEC; - $e - } - $crate::SpecId::CANYON => { - use $crate::CanyonSpec as SPEC; - $e - } - $crate::SpecId::ECOTONE => { - use $crate::EcotoneSpec as SPEC; - $e - } - $crate::SpecId::FJORD => { - use $crate::FjordSpec as SPEC; - $e - } } }}; } @@ -406,138 +244,9 @@ mod tests { spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); - #[cfg(feature = "optimism")] - spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); - #[cfg(feature = "optimism")] - spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); - spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); - #[cfg(feature = "optimism")] - spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); - #[cfg(feature = "optimism")] - spec_to_generic!(ECOTONE, assert_eq!(SPEC::SPEC_ID, ECOTONE)); - #[cfg(feature = "optimism")] - spec_to_generic!(FJORD, assert_eq!(SPEC::SPEC_ID, FJORD)); spec_to_generic!(PRAGUE, assert_eq!(SPEC::SPEC_ID, PRAGUE)); spec_to_generic!(PRAGUE_EOF, assert_eq!(SPEC::SPEC_ID, PRAGUE_EOF)); spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); } } - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod optimism_tests { - use super::*; - - #[test] - fn test_bedrock_post_merge_hardforks() { - assert!(BedrockSpec::enabled(SpecId::MERGE)); - assert!(!BedrockSpec::enabled(SpecId::SHANGHAI)); - assert!(!BedrockSpec::enabled(SpecId::CANCUN)); - assert!(!BedrockSpec::enabled(SpecId::LATEST)); - assert!(BedrockSpec::enabled(SpecId::BEDROCK)); - assert!(!BedrockSpec::enabled(SpecId::REGOLITH)); - } - - #[test] - fn test_regolith_post_merge_hardforks() { - assert!(RegolithSpec::enabled(SpecId::MERGE)); - assert!(!RegolithSpec::enabled(SpecId::SHANGHAI)); - assert!(!RegolithSpec::enabled(SpecId::CANCUN)); - assert!(!RegolithSpec::enabled(SpecId::LATEST)); - assert!(RegolithSpec::enabled(SpecId::BEDROCK)); - assert!(RegolithSpec::enabled(SpecId::REGOLITH)); - } - - #[test] - fn test_bedrock_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::BEDROCK, SpecId::MERGE)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::SHANGHAI)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::BEDROCK, SpecId::BEDROCK)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::REGOLITH)); - } - - #[test] - fn test_regolith_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::MERGE)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::SHANGHAI)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::BEDROCK)); - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::REGOLITH)); - } - - #[test] - fn test_canyon_post_merge_hardforks() { - assert!(CanyonSpec::enabled(SpecId::MERGE)); - assert!(CanyonSpec::enabled(SpecId::SHANGHAI)); - assert!(!CanyonSpec::enabled(SpecId::CANCUN)); - assert!(!CanyonSpec::enabled(SpecId::LATEST)); - assert!(CanyonSpec::enabled(SpecId::BEDROCK)); - assert!(CanyonSpec::enabled(SpecId::REGOLITH)); - assert!(CanyonSpec::enabled(SpecId::CANYON)); - } - - #[test] - fn test_canyon_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::CANYON, SpecId::MERGE)); - assert!(SpecId::enabled(SpecId::CANYON, SpecId::SHANGHAI)); - assert!(!SpecId::enabled(SpecId::CANYON, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::CANYON, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::CANYON, SpecId::BEDROCK)); - assert!(SpecId::enabled(SpecId::CANYON, SpecId::REGOLITH)); - assert!(SpecId::enabled(SpecId::CANYON, SpecId::CANYON)); - } - - #[test] - fn test_ecotone_post_merge_hardforks() { - assert!(EcotoneSpec::enabled(SpecId::MERGE)); - assert!(EcotoneSpec::enabled(SpecId::SHANGHAI)); - assert!(EcotoneSpec::enabled(SpecId::CANCUN)); - assert!(!EcotoneSpec::enabled(SpecId::LATEST)); - assert!(EcotoneSpec::enabled(SpecId::BEDROCK)); - assert!(EcotoneSpec::enabled(SpecId::REGOLITH)); - assert!(EcotoneSpec::enabled(SpecId::CANYON)); - assert!(EcotoneSpec::enabled(SpecId::ECOTONE)); - } - - #[test] - fn test_ecotone_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::MERGE)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::SHANGHAI)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::ECOTONE, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::BEDROCK)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::REGOLITH)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::CANYON)); - assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::ECOTONE)); - } - - #[test] - fn test_fjord_post_merge_hardforks() { - assert!(FjordSpec::enabled(SpecId::MERGE)); - assert!(FjordSpec::enabled(SpecId::SHANGHAI)); - assert!(FjordSpec::enabled(SpecId::CANCUN)); - assert!(!FjordSpec::enabled(SpecId::LATEST)); - assert!(FjordSpec::enabled(SpecId::BEDROCK)); - assert!(FjordSpec::enabled(SpecId::REGOLITH)); - assert!(FjordSpec::enabled(SpecId::CANYON)); - assert!(FjordSpec::enabled(SpecId::ECOTONE)); - assert!(FjordSpec::enabled(SpecId::FJORD)); - } - - #[test] - fn test_fjord_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::FJORD, SpecId::MERGE)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::SHANGHAI)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::FJORD, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::BEDROCK)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::REGOLITH)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANYON)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::ECOTONE)); - assert!(SpecId::enabled(SpecId::FJORD, SpecId::FJORD)); - } -} diff --git a/crates/primitives/src/transaction.rs b/crates/primitives/src/transaction.rs new file mode 100644 index 0000000000..65602cef31 --- /dev/null +++ b/crates/primitives/src/transaction.rs @@ -0,0 +1,58 @@ +use crate::{AccessListItem, Address, Bytes, TxKind, B256, GAS_PER_BLOB, U256}; + +/// Trait for retrieving transaction information required for execution. +pub trait Transaction { + /// Caller aka Author aka transaction signer. + fn caller(&self) -> &Address; + /// The maximum amount of gas the transaction can use. + fn gas_limit(&self) -> u64; + /// The gas price the sender is willing to pay. + fn gas_price(&self) -> &U256; + /// Returns what kind of transaction this is. + fn kind(&self) -> TxKind; + /// The value sent to the receiver of `TxKind::Call`. + fn value(&self) -> &U256; + /// Returns the input data of the transaction. + fn data(&self) -> &Bytes; + /// The nonce of the transaction. + fn nonce(&self) -> u64; + /// The chain ID of the transaction. If set to `None`, no checks are performed. + /// + /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + fn chain_id(&self) -> Option; + /// A list of addresses and storage keys that the transaction plans to access. + /// + /// Added in [EIP-2930]. + /// + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + fn access_list(&self) -> &[AccessListItem]; + /// The maximum priority fee per gas the sender is willing to pay. + /// + /// Incorporated as part of the London upgrade via [EIP-1559]. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + fn max_priority_fee_per_gas(&self) -> Option<&U256>; + /// The list of blob versioned hashes. Per EIP there should be at least + /// one blob present if [`Self::max_fee_per_blob_gas`] is `Some`. + /// + /// Incorporated as part of the Cancun upgrade via [EIP-4844]. + /// + /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 + fn blob_hashes(&self) -> &[B256]; + /// The maximum fee per blob gas the sender is willing to pay. + /// + /// Incorporated as part of the Cancun upgrade via [EIP-4844]. + /// + /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 + fn max_fee_per_blob_gas(&self) -> Option<&U256>; +} + +/// See [EIP-4844], [`crate::Env::calc_data_fee`], and [`crate::Env::calc_max_data_fee`]. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +pub fn get_total_blob_gas(transaction: &impl Transaction) -> u64 { + GAS_PER_BLOB * transaction.blob_hashes().len() as u64 +} diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index bae8199148..260b31d8e3 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -28,8 +28,9 @@ revm-precompile = { path = "../precompile", version = "8.0.0", default-features # misc auto_impl = { version = "1.2", default-features = false } -cfg-if = "1.0" +derive-where = { version = "1.2.7", default-features = false } dyn-clone = "1.0" +enumn = { version = "0.1", optional = true } # Optional serde = { version = "1.0", default-features = false, features = [ @@ -50,7 +51,7 @@ ethers-core = { version = "2.0", optional = true } # alloydb alloy-provider = { version = "0.1", optional = true, default-features = false } -alloy-eips = { version = "0.1", optional = true, default-features = false } +alloy-eips = { version = "0.1.2", optional = true, default-features = false } alloy-transport = { version = "0.1", optional = true, default-features = false } [dev-dependencies] @@ -84,17 +85,7 @@ portable = ["revm-precompile/portable", "revm-interpreter/portable"] test-utils = [] -optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] -# Optimism default handler enabled Optimism handler register by default in EvmBuilder. -optimism-default-handler = [ - "optimism", - "revm-precompile/optimism-default-handler", - "revm-interpreter/optimism-default-handler", -] -negate-optimism-default-handler = [ - "revm-precompile/negate-optimism-default-handler", - "revm-interpreter/negate-optimism-default-handler", -] +optimism = ["dep:enumn", "revm-interpreter/optimism", "revm-precompile/optimism"] ethersdb = ["std", "dep:tokio", "dep:ethers-providers", "dep:ethers-core"] @@ -114,6 +105,7 @@ dev = [ "optional_gas_refund", "optional_no_base_fee", "optional_beneficiary_reward", + "optional_nonce_check", ] memory_limit = ["revm-interpreter/memory_limit"] optional_balance_check = ["revm-interpreter/optional_balance_check"] @@ -122,6 +114,7 @@ optional_eip3607 = ["revm-interpreter/optional_eip3607"] optional_gas_refund = ["revm-interpreter/optional_gas_refund"] optional_no_base_fee = ["revm-interpreter/optional_no_base_fee"] optional_beneficiary_reward = ["revm-interpreter/optional_beneficiary_reward"] +optional_nonce_check = ["revm-interpreter/optional_nonce_check"] # See comments in `revm-precompile` secp256k1 = ["revm-precompile/secp256k1"] diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index a135a367d2..bdba5206c0 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -4,7 +4,7 @@ use criterion::{ use revm::{ db::BenchmarkDB, interpreter::{analysis::to_analysed, Contract, DummyHost, Interpreter}, - primitives::{address, bytes, hex, BerlinSpec, Bytecode, Bytes, TxKind, U256}, + primitives::{address, bytes, hex, BerlinSpec, Bytecode, Bytes, EthChainSpec, TxKind, U256}, Evm, }; use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; @@ -81,7 +81,10 @@ fn transfer(c: &mut Criterion) { g.finish(); } -fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'_, EXT, BenchmarkDB>) { +fn bench_transact( + g: &mut BenchmarkGroup<'_, WallTime>, + evm: &mut Evm<'_, EthChainSpec, EXT, BenchmarkDB>, +) { let state = match evm.context.evm.db.0 { Bytecode::LegacyRaw(_) => "raw", Bytecode::LegacyAnalyzed(_) => "analysed", @@ -91,7 +94,10 @@ fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'_, E g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); } -fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), BenchmarkDB>) { +fn bench_eval( + g: &mut BenchmarkGroup<'_, WallTime>, + evm: &mut Evm<'static, EthChainSpec, (), BenchmarkDB>, +) { g.bench_function("eval", |b| { let contract = Contract { input: evm.context.evm.env.tx.data.clone(), @@ -100,7 +106,7 @@ fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), B }; let mut shared_memory = SharedMemory::new(); let mut host = DummyHost::new(*evm.context.evm.env.clone()); - let instruction_table = make_instruction_table::(); + let instruction_table = make_instruction_table::, BerlinSpec>(); b.iter(move || { // replace memory with empty memory to use it inside interpreter. // Later return memory back. diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index c66b61f556..13a3a3fba7 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -1,10 +1,11 @@ use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, - handler::register, - primitives::{ - BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, HandlerCfg, SpecId, TxEnv, + handler::{ + register::{self, EvmHandler}, + CfgEnvWithChainSpec, EnvWithChainSpec, }, - Context, ContextWithHandlerCfg, Evm, Handler, + primitives::{CfgEnv, ChainSpec, Env, EthChainSpec, InvalidTransaction, TransactionValidation}, + Context, ContextWithChainSpec, Evm, EvmContext, Handler, }; use core::marker::PhantomData; use std::boxed::Box; @@ -12,10 +13,10 @@ use std::boxed::Box; /// Evm Builder allows building or modifying EVM. /// Note that some of the methods that changes underlying structures /// will reset the registered handler to default mainnet. -pub struct EvmBuilder<'a, BuilderStage, EXT, DB: Database> { - context: Context, +pub struct EvmBuilder<'a, BuilderStage, ChainSpecT: ChainSpec, EXT, DB: Database> { + context: Context, /// Handler that will be used by EVM. It contains handle registers - handler: Handler<'a, Context, EXT, DB>, + handler: Handler<'a, ChainSpecT, Context, EXT, DB>, /// Phantom data to mark the stage of the builder. phantom: PhantomData, } @@ -28,45 +29,68 @@ pub struct SetGenericStage; /// Requires the database and external context to be set. pub struct HandlerStage; -impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { +impl<'a> Default for EvmBuilder<'a, SetGenericStage, EthChainSpec, (), EmptyDB> { fn default() -> Self { - cfg_if::cfg_if! { - if #[cfg(all(feature = "optimism-default-handler", - not(feature = "negate-optimism-default-handler")))] { - let mut handler_cfg = HandlerCfg::new(SpecId::LATEST); - // set is_optimism to true by default. - handler_cfg.is_optimism = true; - - } else { - let handler_cfg = HandlerCfg::new(SpecId::LATEST); - } - } - Self { context: Context::default(), - handler: EvmBuilder::<'a, SetGenericStage, (), EmptyDB>::handler(handler_cfg), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler( + ::Hardfork::default(), + ), phantom: PhantomData, } } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { +impl<'a, ChainSpecT, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, DB> +where + ChainSpecT: ChainSpec, +{ + /// Sets the [`ChainSpec`] that will be used by [`Evm`]. + pub fn with_chain_spec( + self, + ) -> EvmBuilder<'a, SetGenericStage, NewChainSpecT, EXT, DB> + where + NewChainSpecT: ChainSpec< + Block: Default, + Transaction: Default + TransactionValidation>, + >, + { + let Context { evm, external } = self.context; + + EvmBuilder { + context: Context::new(EvmContext::new(evm.inner.db), external), + handler: EvmBuilder::<'_, SetGenericStage, NewChainSpecT, _, _>::handler( + NewChainSpecT::Hardfork::default(), + ), + phantom: PhantomData, + } + } +} + +impl<'a, ChainSpecT, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, DB> +where + ChainSpecT: + ChainSpec>>, +{ /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. - pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> { + pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, EmptyDB> { EvmBuilder { context: Context::new( self.context.evm.with_db(EmptyDB::default()), self.context.external, ), - handler: EvmBuilder::<'a, SetGenericStage, EXT, EmptyDB>::handler(self.handler.cfg()), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } /// Sets the [`Database`] that will be used by [`Evm`]. - pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { + pub fn with_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, ODB> { EvmBuilder { context: Context::new(self.context.evm.with_db(db), self.context.external), - handler: EvmBuilder::<'a, SetGenericStage, EXT, ODB>::handler(self.handler.cfg()), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } @@ -74,15 +98,13 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { pub fn with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, WrapDatabaseRef> { EvmBuilder { context: Context::new( self.context.evm.with_db(WrapDatabaseRef(db)), self.context.external, ), - handler: EvmBuilder::<'a, SetGenericStage, EXT, WrapDatabaseRef>::handler( - self.handler.cfg(), - ), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } @@ -91,131 +113,88 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { pub fn with_external_context( self, external: OEXT, - ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, OEXT, DB> { EvmBuilder { context: Context::new(self.context.evm, external), - handler: EvmBuilder::<'a, SetGenericStage, OEXT, DB>::handler(self.handler.cfg()), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } - /// Sets Builder with [`EnvWithHandlerCfg`]. + /// Sets Builder with [`EnvWithChainSpec`]. pub fn with_env_with_handler_cfg( mut self, - env_with_handler_cfg: EnvWithHandlerCfg, - ) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - let EnvWithHandlerCfg { env, handler_cfg } = env_with_handler_cfg; + env_with_handler_cfg: EnvWithChainSpec, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> { + let EnvWithChainSpec { env, spec_id } = env_with_handler_cfg; self.context.evm.env = env; EvmBuilder { context: self.context, - handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler(handler_cfg), + handler: EvmBuilder::<'_, HandlerStage, _, _, _>::handler(spec_id), phantom: PhantomData, } } - /// Sets Builder with [`ContextWithHandlerCfg`]. + /// Sets Builder with [`ContextWithChainSpec`]. pub fn with_context_with_handler_cfg( self, - context_with_handler_cfg: ContextWithHandlerCfg, - ) -> EvmBuilder<'a, HandlerStage, OEXT, ODB> { + context_with_handler_cfg: ContextWithChainSpec, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, OEXT, ODB> { EvmBuilder { context: context_with_handler_cfg.context, - handler: EvmBuilder::<'a, HandlerStage, OEXT, ODB>::handler( - context_with_handler_cfg.cfg, + handler: EvmBuilder::<'_, HandlerStage, _, _, _>::handler( + context_with_handler_cfg.spec_id, ), phantom: PhantomData, } } - /// Sets Builder with [`CfgEnvWithHandlerCfg`]. + /// Sets Builder with [`CfgEnvWithChainSpec`]. pub fn with_cfg_env_with_handler_cfg( mut self, - cfg_env_and_spec_id: CfgEnvWithHandlerCfg, - ) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + cfg_env_and_spec_id: CfgEnvWithChainSpec, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> { self.context.evm.env.cfg = cfg_env_and_spec_id.cfg_env; EvmBuilder { context: self.context, - handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler( - cfg_env_and_spec_id.handler_cfg, - ), - phantom: PhantomData, - } - } - - /// Sets Builder with [`HandlerCfg`] - pub fn with_handler_cfg( - self, - handler_cfg: HandlerCfg, - ) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - EvmBuilder { - context: self.context, - handler: EvmBuilder::<'a, HandlerStage, EXT, DB>::handler(handler_cfg), - phantom: PhantomData, - } - } - - /// Sets the Optimism handler with latest spec. - /// - /// If `optimism-default-handler` feature is enabled this is not needed. - #[cfg(feature = "optimism")] - pub fn optimism(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - self.handler = Handler::optimism_with_spec(self.handler.cfg.spec_id); - EvmBuilder { - context: self.context, - handler: self.handler, - phantom: PhantomData, - } - } - - /// Sets the mainnet handler with latest spec. - /// - /// Enabled only with `optimism-default-handler` feature. - #[cfg(feature = "optimism-default-handler")] - pub fn mainnet(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - self.handler = Handler::mainnet_with_spec(self.handler.cfg.spec_id); - EvmBuilder { - context: self.context, - handler: self.handler, + handler: EvmBuilder::<'_, HandlerStage, _, _, _>::handler(cfg_env_and_spec_id.spec_id), phantom: PhantomData, } } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> + EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> +{ /// Creates new builder from Evm, Evm is consumed and all field are moved to Builder. /// It will preserve set handler and context. /// /// Builder is in HandlerStage and both database and external are set. - pub fn new(evm: Evm<'a, EXT, DB>) -> Self { + pub fn new(evm: Evm<'a, ChainSpecT, EXT, DB>) -> Self { Self { context: evm.context, handler: evm.handler, phantom: PhantomData, } } +} +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> +where + ChainSpecT: + ChainSpec>>, +{ /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet. - pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { + pub fn reset_handler_with_empty_db( + self, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, EmptyDB> { EvmBuilder { context: Context::new( self.context.evm.with_db(EmptyDB::default()), self.context.external, ), - handler: EvmBuilder::<'a, HandlerStage, EXT, EmptyDB>::handler(self.handler.cfg()), - phantom: PhantomData, - } - } - - /// Resets the [`Handler`] and sets base mainnet handler. - /// - /// Enabled only with `optimism-default-handler` feature. - #[cfg(feature = "optimism-default-handler")] - pub fn reset_handler_with_mainnet(mut self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - self.handler = Handler::mainnet_with_spec(self.handler.cfg.spec_id); - EvmBuilder { - context: self.context, - handler: self.handler, + handler: EvmBuilder::<'_, HandlerStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } @@ -225,10 +204,10 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { pub fn reset_handler_with_db( self, db: ODB, - ) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, ODB> { EvmBuilder { context: Context::new(self.context.evm.with_db(db), self.context.external), - handler: EvmBuilder::<'a, SetGenericStage, EXT, ODB>::handler(self.handler.cfg()), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } @@ -238,15 +217,13 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { pub fn reset_handler_with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, EXT, WrapDatabaseRef> { EvmBuilder { context: Context::new( self.context.evm.with_db(WrapDatabaseRef(db)), self.context.external, ), - handler: EvmBuilder::<'a, SetGenericStage, EXT, WrapDatabaseRef>::handler( - self.handler.cfg(), - ), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } @@ -256,34 +233,29 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { pub fn reset_handler_with_external_context( self, external: OEXT, - ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { + ) -> EvmBuilder<'a, SetGenericStage, ChainSpecT, OEXT, DB> { EvmBuilder { context: Context::new(self.context.evm, external), - handler: EvmBuilder::<'a, SetGenericStage, OEXT, DB>::handler(self.handler.cfg()), + handler: EvmBuilder::<'_, SetGenericStage, _, _, _>::handler(self.handler.spec_id()), phantom: PhantomData, } } } -impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> { - /// Creates the default handler. - /// - /// This is useful for adding optimism handle register. - fn handler(handler_cfg: HandlerCfg) -> Handler<'a, Context, EXT, DB> { - Handler::new(handler_cfg) - } - +impl<'a, BuilderStage, ChainSpecT: ChainSpec, EXT, DB: Database> + EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> +{ /// This modifies the [EvmBuilder] to make it easy to construct an [`Evm`] with a _specific_ /// handler. /// /// # Example /// ```rust - /// use revm::{EvmBuilder, Handler, primitives::{SpecId, HandlerCfg}}; + /// use revm::{EvmBuilder, EvmHandler, db::EmptyDB, primitives::{EthChainSpec, SpecId}}; /// use revm_interpreter::primitives::CancunSpec; /// let builder = EvmBuilder::default(); /// /// // get the desired handler - /// let mainnet = Handler::mainnet::(); + /// let mainnet = EvmHandler::<'_, EthChainSpec, (), EmptyDB>::mainnet_with_spec(SpecId::CANCUN); /// let builder = builder.with_handler(mainnet); /// /// // build the EVM @@ -291,8 +263,8 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> /// ``` pub fn with_handler( self, - handler: Handler<'a, Context, EXT, DB>, - ) -> EvmBuilder<'a, BuilderStage, EXT, DB> { + handler: Handler<'a, ChainSpecT, Context, EXT, DB>, + ) -> EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> { EvmBuilder { context: self.context, handler, @@ -301,7 +273,7 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> } /// Builds the [`Evm`]. - pub fn build(self) -> Evm<'a, EXT, DB> { + pub fn build(self) -> Evm<'a, ChainSpecT, EXT, DB> { Evm::new(self.context, self.handler) } @@ -311,8 +283,8 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. pub fn append_handler_register( mut self, - handle_register: register::HandleRegister, - ) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + handle_register: register::HandleRegister, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> { self.handler .append_handler_register(register::HandleRegisters::Plain(handle_register)); EvmBuilder { @@ -329,8 +301,8 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. pub fn append_handler_register_box( mut self, - handle_register: register::HandleRegisterBox, - ) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + handle_register: register::HandleRegisterBox, + ) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> { self.handler .append_handler_register(register::HandleRegisters::Box(handle_register)); EvmBuilder { @@ -341,23 +313,6 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> } } - /// Sets specification Id , that will mark the version of EVM. - /// It represent the hard fork of ethereum. - /// - /// # Note - /// - /// When changed it will reapply all handle registers, this can be - /// expensive operation depending on registers. - pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - self.handler.modify_spec_id(spec_id); - EvmBuilder { - context: self.context, - handler: self.handler, - - phantom: PhantomData, - } - } - /// Allows modification of Evm Database. pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { f(&mut self.context.evm.db); @@ -371,37 +326,37 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> } /// Allows modification of Evm Environment. - pub fn modify_env(mut self, f: impl FnOnce(&mut Box)) -> Self { + pub fn modify_env(mut self, f: impl FnOnce(&mut Box>)) -> Self { f(&mut self.context.evm.env); self } /// Sets Evm Environment. - pub fn with_env(mut self, env: Box) -> Self { + pub fn with_env(mut self, env: Box>) -> Self { self.context.evm.env = env; self } /// Allows modification of Evm's Transaction Environment. - pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self { + pub fn modify_tx_env(mut self, f: impl FnOnce(&mut ChainSpecT::Transaction)) -> Self { f(&mut self.context.evm.env.tx); self } /// Sets Evm's Transaction Environment. - pub fn with_tx_env(mut self, tx_env: TxEnv) -> Self { + pub fn with_tx_env(mut self, tx_env: ChainSpecT::Transaction) -> Self { self.context.evm.env.tx = tx_env; self } /// Allows modification of Evm's Block Environment. - pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self { + pub fn modify_block_env(mut self, f: impl FnOnce(&mut ChainSpecT::Block)) -> Self { f(&mut self.context.evm.env.block); self } /// Sets Evm's Block Environment. - pub fn with_block_env(mut self, block_env: BlockEnv) -> Self { + pub fn with_block_env(mut self, block_env: ChainSpecT::Block) -> Self { self.context.evm.env.block = block_env; self } @@ -411,40 +366,89 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> f(&mut self.context.evm.env.cfg); self } +} - /// Clears Environment of EVM. - pub fn with_clear_env(mut self) -> Self { - self.context.evm.env.clear(); +impl<'a, BuilderStage, ChainSpecT, EXT, DB> EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> +where + ChainSpecT: ChainSpec, + DB: Database, +{ + /// Clears Block environment of EVM. + pub fn with_clear_block_env(mut self) -> Self { + self.context.evm.env.block = ChainSpecT::Block::default(); self } +} +impl<'a, BuilderStage, ChainSpecT, EXT, DB> EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> +where + ChainSpecT: ChainSpec, + DB: Database, +{ /// Clears Transaction environment of EVM. pub fn with_clear_tx_env(mut self) -> Self { - self.context.evm.env.tx.clear(); + self.context.evm.env.tx = ChainSpecT::Transaction::default(); self } - /// Clears Block environment of EVM. - pub fn with_clear_block_env(mut self) -> Self { - self.context.evm.env.block.clear(); +} + +impl<'a, BuilderStage, ChainSpecT, EXT, DB> EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> +where + ChainSpecT: ChainSpec, + DB: Database, +{ + /// Clears Environment of EVM. + pub fn with_clear_env(mut self) -> Self { + self.context.evm.env.clear(); self } +} + +impl<'a, BuilderStage, ChainSpecT: ChainSpec, EXT, DB: Database> + EvmBuilder<'a, BuilderStage, ChainSpecT, EXT, DB> +where + ChainSpecT: + ChainSpec>>, +{ + /// Creates the default handler. + /// + /// This is useful for adding optimism handle register. + fn handler(spec_id: ChainSpecT::Hardfork) -> EvmHandler<'a, ChainSpecT, EXT, DB> { + Handler::mainnet_with_spec(spec_id) + } + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + /// + /// # Note + /// + /// When changed it will reapply all handle registers, this can be + /// expensive operation depending on registers. + pub fn with_spec_id(mut self, spec_id: ChainSpecT::Hardfork) -> Self { + self.handler.modify_spec_id(spec_id); + EvmBuilder { + context: self.context, + handler: self.handler, + + phantom: PhantomData, + } + } /// Resets [`Handler`] to default mainnet. pub fn reset_handler(mut self) -> Self { - self.handler = Self::handler(self.handler.cfg()); + self.handler = Self::handler(self.handler.spec_id()); self } } #[cfg(test)] mod test { - use super::SpecId; use crate::{ db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, primitives::{ - address, AccountInfo, Address, Bytecode, Bytes, PrecompileResult, TxKind, U256, + address, AccountInfo, Address, Bytecode, Bytes, PrecompileResult, SpecId, TxKind, U256, }, Context, ContextPrecompile, ContextStatefulPrecompile, Evm, InMemoryDB, InnerEvmContext, }; @@ -452,6 +456,11 @@ mod test { use revm_precompile::PrecompileOutput; use std::{cell::RefCell, rc::Rc, sync::Arc}; + #[cfg(feature = "optimism")] + type TestChainSpec = crate::optimism::OptimismChainSpec; + #[cfg(not(feature = "optimism"))] + type TestChainSpec = crate::primitives::EthChainSpec; + /// Custom evm context #[derive(Default, Clone, Debug)] pub(crate) struct CustomContext { @@ -470,11 +479,19 @@ mod test { let to_capture = custom_context.clone(); let mut evm = Evm::builder() + .with_chain_spec::() .with_db(InMemoryDB::default()) .modify_db(|db| { db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code)) }) - .modify_tx_env(|tx| tx.transact_to = TxKind::Call(to_addr)) + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let transact_to = &mut tx.base.transact_to; + #[cfg(not(feature = "optimism"))] + let transact_to = &mut tx.transact_to; + + *transact_to = TxKind::Call(to_addr) + }) // we need to use handle register box to capture the custom context in the handle // register .append_handler_register_box(Box::new(move |handler| { @@ -482,7 +499,8 @@ mod test { // we need to use a box to capture the custom context in the instruction let custom_instruction = Box::new( - move |_interp: &mut Interpreter, _host: &mut Context<(), InMemoryDB>| { + move |_interp: &mut Interpreter, + _host: &mut Context| { // modify the value let mut inner = custom_context.inner.borrow_mut(); *inner += 1; @@ -519,11 +537,19 @@ mod test { let to_addr = address!("ffffffffffffffffffffffffffffffffffffffff"); let mut evm = Evm::builder() + .with_chain_spec::() .with_db(InMemoryDB::default()) .modify_db(|db| { db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code)) }) - .modify_tx_env(|tx| tx.transact_to = TxKind::Call(to_addr)) + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let transact_to = &mut tx.base.transact_to; + #[cfg(not(feature = "optimism"))] + let transact_to = &mut tx.transact_to; + + *transact_to = TxKind::Call(to_addr) + }) .append_handler_register(|handler| { handler.instruction_table.insert(0xEF, custom_instruction) }) @@ -536,15 +562,25 @@ mod test { #[test] fn simple_build() { // build without external with latest spec - Evm::builder().build(); + Evm::builder().with_chain_spec::().build(); // build with empty db - Evm::builder().with_empty_db().build(); + Evm::builder() + .with_chain_spec::() + .with_empty_db() + .build(); // build with_db - Evm::builder().with_db(EmptyDB::default()).build(); + Evm::builder() + .with_chain_spec::() + .with_db(EmptyDB::default()) + .build(); // build with empty external - Evm::builder().with_empty_db().build(); + Evm::builder() + .with_chain_spec::() + .with_empty_db() + .build(); // build with some external Evm::builder() + .with_chain_spec::() .with_empty_db() .with_external_context(()) .build(); @@ -556,21 +592,56 @@ mod test { // with with Env change in multiple places Evm::builder() + .with_chain_spec::() .with_empty_db() - .modify_tx_env(|tx| tx.gas_limit = 10) + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let gas_limit = &mut tx.base.gas_limit; + #[cfg(not(feature = "optimism"))] + let gas_limit = &mut tx.gas_limit; + + *gas_limit = 10 + }) .build(); - Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); Evm::builder() + .with_chain_spec::() + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let gas_limit = &mut tx.base.gas_limit; + #[cfg(not(feature = "optimism"))] + let gas_limit = &mut tx.gas_limit; + + *gas_limit = 10 + }) + .build(); + Evm::builder() + .with_chain_spec::() .with_empty_db() - .modify_tx_env(|tx| tx.gas_limit = 10) + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let gas_limit = &mut tx.base.gas_limit; + #[cfg(not(feature = "optimism"))] + let gas_limit = &mut tx.gas_limit; + + *gas_limit = 10 + }) .build(); Evm::builder() + .with_chain_spec::() .with_empty_db() - .modify_tx_env(|tx| tx.gas_limit = 10) + .modify_tx_env(|tx| { + #[cfg(feature = "optimism")] + let gas_limit = &mut tx.base.gas_limit; + #[cfg(not(feature = "optimism"))] + let gas_limit = &mut tx.gas_limit; + + *gas_limit = 10 + }) .build(); // with inspector handle Evm::builder() + .with_chain_spec::() .with_empty_db() .with_external_context(NoOpInspector) .append_handler_register(inspector_handle_register) @@ -579,6 +650,7 @@ mod test { // create the builder let evm = Evm::builder() .with_db(EmptyDB::default()) + .with_chain_spec::() .with_external_context(NoOpInspector) .append_handler_register(inspector_handle_register) // this would not compile @@ -608,20 +680,25 @@ mod test { fn build_custom_precompile() { struct CustomPrecompile; - impl ContextStatefulPrecompile for CustomPrecompile { + impl ContextStatefulPrecompile for CustomPrecompile { fn call( &self, _input: &Bytes, _gas_price: u64, - _context: &mut InnerEvmContext, + _context: &mut InnerEvmContext, ) -> PrecompileResult { Ok(PrecompileOutput::new(10, Bytes::new())) } } + #[cfg(feature = "optimism")] + let spec_id = crate::optimism::OptimismSpecId::HOMESTEAD; + #[cfg(not(feature = "optimism"))] + let spec_id = crate::primitives::SpecId::HOMESTEAD; + let mut evm = Evm::builder() - .with_empty_db() - .with_spec_id(SpecId::HOMESTEAD) + .with_chain_spec::() + .with_spec_id(spec_id) .append_handler_register(|handler| { let precompiles = handler.pre_execution.load_precompiles(); handler.pre_execution.load_precompiles = Arc::new(move || { diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index d715296b43..fecb35a960 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -13,21 +13,27 @@ use revm_interpreter::as_usize_saturated; use crate::{ db::{Database, EmptyDB}, interpreter::{Host, LoadAccountResult, SStoreResult, SelfDestructResult}, - primitives::{Address, Bytes, Env, HandlerCfg, Log, B256, BLOCK_HASH_HISTORY, U256}, + primitives::{ + Address, Block as _, Bytes, ChainSpec, Env, EthChainSpec, Log, B256, BLOCK_HASH_HISTORY, + U256, + }, }; use std::boxed::Box; /// Main Context structure that contains both EvmContext and External context. -pub struct Context { +pub struct Context { /// Evm Context (internal context). - pub evm: EvmContext, + pub evm: EvmContext, /// External contexts. pub external: EXT, } -impl Clone for Context +impl Clone for Context where DB::Error: Clone, + ChainSpecT: ChainSpec, + EXT: Clone, + DB: Database + Clone, { fn clone(&self) -> Self { Self { @@ -37,15 +43,18 @@ where } } -impl Default for Context<(), EmptyDB> { +impl Default for Context { fn default() -> Self { Self::new_empty() } } -impl Context<(), EmptyDB> { +impl Context +where + ChainSpecT: ChainSpec, +{ /// Creates empty context. This is useful for testing. - pub fn new_empty() -> Context<(), EmptyDB> { + pub fn new_empty() -> Context { Context { evm: EvmContext::new(EmptyDB::new()), external: (), @@ -53,9 +62,13 @@ impl Context<(), EmptyDB> { } } -impl Context<(), DB> { +impl Context +where + ChainSpecT: ChainSpec, + DB: Database, +{ /// Creates new context with database. - pub fn new_with_db(db: DB) -> Context<(), DB> { + pub fn new_with_db(db: DB) -> Context { Context { evm: EvmContext::new_with_env(db, Box::default()), external: (), @@ -63,53 +76,57 @@ impl Context<(), DB> { } } -impl Context { +impl Context { /// Creates new context with external and database. - pub fn new(evm: EvmContext, external: EXT) -> Context { + pub fn new(evm: EvmContext, external: EXT) -> Context { Context { evm, external } } } /// Context with handler configuration. -pub struct ContextWithHandlerCfg { +pub struct ContextWithChainSpec { /// Context of execution. - pub context: Context, + pub context: Context, /// Handler configuration. - pub cfg: HandlerCfg, + pub spec_id: ChainSpecT::Hardfork, } -impl ContextWithHandlerCfg { +impl ContextWithChainSpec { /// Creates new context with handler configuration. - pub fn new(context: Context, cfg: HandlerCfg) -> Self { - Self { cfg, context } + pub fn new(context: Context, spec_id: ChainSpecT::Hardfork) -> Self { + Self { spec_id, context } } } -impl Clone for ContextWithHandlerCfg +impl Clone for ContextWithChainSpec where - DB::Error: Clone, + ChainSpecT: ChainSpec, + EXT: Clone, + DB: Database + Clone, { fn clone(&self) -> Self { Self { context: self.context.clone(), - cfg: self.cfg, + spec_id: self.spec_id, } } } -impl Host for Context { +impl Host for Context { + type ChainSpecT = ChainSpecT; + /// Returns reference to Environment. #[inline] - fn env(&self) -> &Env { + fn env(&self) -> &Env { &self.evm.env } - fn env_mut(&mut self) -> &mut Env { + fn env_mut(&mut self) -> &mut Env { &mut self.evm.env } fn block_hash(&mut self, number: u64) -> Option { - let block_number = as_usize_saturated!(self.env().block.number); + let block_number = as_usize_saturated!(self.env().block.number()); let requested_number = usize::try_from(number).unwrap_or(usize::MAX); let Some(diff) = block_number.checked_sub(requested_number) else { diff --git a/crates/revm/src/context/context_precompiles.rs b/crates/revm/src/context/context_precompiles.rs index a47cef7132..447662f974 100644 --- a/crates/revm/src/context/context_precompiles.rs +++ b/crates/revm/src/context/context_precompiles.rs @@ -1,63 +1,52 @@ use super::InnerEvmContext; use crate::{ precompile::{Precompile, PrecompileResult}, - primitives::{db::Database, Address, Bytes, HashMap, HashSet}, + primitives::{db::Database, Address, Bytes, ChainSpec, HashMap, HashSet}, }; +use core::fmt::Debug; +use derive_where::derive_where; use dyn_clone::DynClone; use revm_precompile::{PrecompileSpecId, PrecompileWithAddress, Precompiles}; use std::{boxed::Box, sync::Arc}; /// A single precompile handler. -pub enum ContextPrecompile { +#[derive_where(Clone)] +pub enum ContextPrecompile { /// Ordinary precompiles Ordinary(Precompile), /// Stateful precompile that is Arc over [`ContextStatefulPrecompile`] trait. /// It takes a reference to input, gas limit and Context. - ContextStateful(ContextStatefulPrecompileArc), + ContextStateful(ContextStatefulPrecompileArc), /// Mutable stateful precompile that is Box over [`ContextStatefulPrecompileMut`] trait. /// It takes a reference to input, gas limit and context. - ContextStatefulMut(ContextStatefulPrecompileBox), + ContextStatefulMut(ContextStatefulPrecompileBox), } -impl Clone for ContextPrecompile { - fn clone(&self) -> Self { +impl Debug for ContextPrecompile { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::Ordinary(p) => Self::Ordinary(p.clone()), - Self::ContextStateful(p) => Self::ContextStateful(p.clone()), - Self::ContextStatefulMut(p) => Self::ContextStatefulMut(p.clone()), + Self::Ordinary(arg0) => f.debug_tuple("Ordinary").field(arg0).finish(), + Self::ContextStateful(_arg0) => f.debug_tuple("ContextStateful").finish(), + Self::ContextStatefulMut(_arg0) => f.debug_tuple("ContextStatefulMut").finish(), } } } -enum PrecompilesCow { +#[derive_where(Clone, Debug)] +enum PrecompilesCow { /// Default precompiles, returned by `Precompiles::new`. Used to fast-path the default case. StaticRef(&'static Precompiles), - Owned(HashMap>), -} - -impl Clone for PrecompilesCow { - fn clone(&self) -> Self { - match *self { - PrecompilesCow::StaticRef(p) => PrecompilesCow::StaticRef(p), - PrecompilesCow::Owned(ref inner) => PrecompilesCow::Owned(inner.clone()), - } - } + Owned(HashMap>), } /// Precompiles context. -pub struct ContextPrecompiles { - inner: PrecompilesCow, -} -impl Clone for ContextPrecompiles { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } +#[derive_where(Clone, Debug, Default)] +pub struct ContextPrecompiles { + inner: PrecompilesCow, } -impl ContextPrecompiles { +impl ContextPrecompiles { /// Creates a new precompiles context at the given spec ID. /// /// This is a cheap operation that does not allocate by reusing the global precompiles. @@ -79,7 +68,9 @@ impl ContextPrecompiles { /// Creates a new precompiles context from the given precompiles. #[inline] - pub fn from_precompiles(precompiles: HashMap>) -> Self { + pub fn from_precompiles( + precompiles: HashMap>, + ) -> Self { Self { inner: PrecompilesCow::Owned(precompiles), } @@ -120,12 +111,14 @@ impl ContextPrecompiles { address: &Address, bytes: &Bytes, gas_price: u64, - evmctx: &mut InnerEvmContext, + evmctx: &mut InnerEvmContext, ) -> Option { Some(match self.inner { - PrecompilesCow::StaticRef(p) => p.get(address)?.call_ref(bytes, gas_price, &evmctx.env), + PrecompilesCow::StaticRef(p) => { + p.get(address)?.call_ref(bytes, gas_price, &evmctx.env.cfg) + } PrecompilesCow::Owned(ref mut owned) => match owned.get_mut(address)? { - ContextPrecompile::Ordinary(p) => p.call(bytes, gas_price, &evmctx.env), + ContextPrecompile::Ordinary(p) => p.call(bytes, gas_price, &evmctx.env.cfg), ContextPrecompile::ContextStateful(p) => p.call(bytes, gas_price, evmctx), ContextPrecompile::ContextStatefulMut(p) => p.call_mut(bytes, gas_price, evmctx), }, @@ -136,7 +129,7 @@ impl ContextPrecompiles { /// /// Clones the precompiles map if it is shared. #[inline] - pub fn to_mut(&mut self) -> &mut HashMap> { + pub fn to_mut(&mut self) -> &mut HashMap> { if let PrecompilesCow::StaticRef(_) = self.inner { self.mutate_into_owned(); } @@ -164,13 +157,20 @@ impl ContextPrecompiles { } } -impl Extend<(Address, ContextPrecompile)> for ContextPrecompiles { - fn extend)>>(&mut self, iter: T) { +impl Extend<(Address, ContextPrecompile)> + for ContextPrecompiles +{ + fn extend)>>( + &mut self, + iter: T, + ) { self.to_mut().extend(iter.into_iter().map(Into::into)) } } -impl Extend for ContextPrecompiles { +impl Extend + for ContextPrecompiles +{ fn extend>(&mut self, iter: T) { self.to_mut().extend(iter.into_iter().map(|precompile| { let (address, precompile) = precompile.into(); @@ -179,15 +179,7 @@ impl Extend for ContextPrecompiles { } } -impl Default for ContextPrecompiles { - fn default() -> Self { - Self { - inner: Default::default(), - } - } -} - -impl Default for PrecompilesCow { +impl Default for PrecompilesCow { fn default() -> Self { Self::Owned(Default::default()) } @@ -195,35 +187,39 @@ impl Default for PrecompilesCow { /// Context aware stateful precompile trait. It is used to create /// a arc precompile in [`ContextPrecompile`]. -pub trait ContextStatefulPrecompile: Sync + Send { +pub trait ContextStatefulPrecompile: Sync + Send { fn call( &self, bytes: &Bytes, gas_price: u64, - evmctx: &mut InnerEvmContext, + evmctx: &mut InnerEvmContext, ) -> PrecompileResult; } /// Context aware mutable stateful precompile trait. It is used to create /// a boxed precompile in [`ContextPrecompile`]. -pub trait ContextStatefulPrecompileMut: DynClone + Send + Sync { +pub trait ContextStatefulPrecompileMut: + DynClone + Send + Sync +{ fn call_mut( &mut self, bytes: &Bytes, gas_price: u64, - evmctx: &mut InnerEvmContext, + evmctx: &mut InnerEvmContext, ) -> PrecompileResult; } -dyn_clone::clone_trait_object!( ContextStatefulPrecompileMut); +dyn_clone::clone_trait_object!( ContextStatefulPrecompileMut); /// Arc over context stateful precompile. -pub type ContextStatefulPrecompileArc = Arc>; +pub type ContextStatefulPrecompileArc = + Arc>; /// Box over context mutable stateful precompile -pub type ContextStatefulPrecompileBox = Box>; +pub type ContextStatefulPrecompileBox = + Box>; -impl From for ContextPrecompile { +impl From for ContextPrecompile { fn from(p: Precompile) -> Self { ContextPrecompile::Ordinary(p) } @@ -232,13 +228,14 @@ impl From for ContextPrecompile { #[cfg(test)] mod tests { use super::*; - use crate::db::EmptyDB; + use crate::{db::EmptyDB, primitives::EthChainSpec}; #[test] fn test_precompiles_context() { let custom_address = Address::with_last_byte(0xff); - let mut precompiles = ContextPrecompiles::::new(PrecompileSpecId::HOMESTEAD); + let mut precompiles = + ContextPrecompiles::::new(PrecompileSpecId::HOMESTEAD); assert_eq!(precompiles.addresses().count(), 4); assert!(matches!(precompiles.inner, PrecompilesCow::StaticRef(_))); assert!(!precompiles.contains(&custom_address)); diff --git a/crates/revm/src/context/evm_context.rs b/crates/revm/src/context/evm_context.rs index e297579b46..5151b82228 100644 --- a/crates/revm/src/context/evm_context.rs +++ b/crates/revm/src/context/evm_context.rs @@ -1,3 +1,4 @@ +use derive_where::derive_where; use revm_interpreter::CallValue; use revm_precompile::PrecompileErrors; @@ -7,63 +8,40 @@ use crate::{ interpreter::{ return_ok, CallInputs, Contract, Gas, InstructionResult, Interpreter, InterpreterResult, }, - primitives::{Address, Bytes, EVMError, Env, U256}, + primitives::{Address, Bytes, ChainSpec, EVMError, EVMResultGeneric, Env, U256}, ContextPrecompiles, FrameOrResult, CALL_STACK_LIMIT, }; -use core::{ - fmt, - ops::{Deref, DerefMut}, -}; +use core::ops::{Deref, DerefMut}; use std::boxed::Box; /// EVM context that contains the inner EVM context and precompiles. -pub struct EvmContext { +#[derive_where(Clone, Debug; ChainSpecT::Block, ChainSpecT::Transaction, DB, DB::Error)] +pub struct EvmContext { /// Inner EVM context. - pub inner: InnerEvmContext, + pub inner: InnerEvmContext, /// Precompiles that are available for evm. - pub precompiles: ContextPrecompiles, + pub precompiles: ContextPrecompiles, } -impl Clone for EvmContext -where - DB::Error: Clone, -{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - precompiles: ContextPrecompiles::default(), - } - } -} - -impl fmt::Debug for EvmContext -where - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EvmContext") - .field("inner", &self.inner) - .field("precompiles", &self.inner) - .finish_non_exhaustive() - } -} - -impl Deref for EvmContext { - type Target = InnerEvmContext; +impl Deref for EvmContext { + type Target = InnerEvmContext; fn deref(&self) -> &Self::Target { &self.inner } } -impl DerefMut for EvmContext { +impl DerefMut for EvmContext { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } -impl EvmContext { +impl EvmContext +where + ChainSpecT: ChainSpec, + DB: Database, +{ /// Create new context with database. pub fn new(db: DB) -> Self { Self { @@ -71,10 +49,12 @@ impl EvmContext { precompiles: ContextPrecompiles::default(), } } +} +impl EvmContext { /// Creates a new context with the given environment and database. #[inline] - pub fn new_with_env(db: DB, env: Box) -> Self { + pub fn new_with_env(db: DB, env: Box>) -> Self { Self { inner: InnerEvmContext::new_with_env(db, env), precompiles: ContextPrecompiles::default(), @@ -85,7 +65,7 @@ impl EvmContext { /// /// Note that this will ignore the previous `error` if set. #[inline] - pub fn with_db(self, db: ODB) -> EvmContext { + pub fn with_db(self, db: ODB) -> EvmContext { EvmContext { inner: self.inner.with_db(db), precompiles: ContextPrecompiles::default(), @@ -94,7 +74,7 @@ impl EvmContext { /// Sets precompiles #[inline] - pub fn set_precompiles(&mut self, precompiles: ContextPrecompiles) { + pub fn set_precompiles(&mut self, precompiles: ContextPrecompiles) { // set warm loaded addresses. self.journaled_state.warm_preloaded_addresses = precompiles.addresses_set(); self.precompiles = precompiles; @@ -107,7 +87,7 @@ impl EvmContext { address: &Address, input_data: &Bytes, gas: Gas, - ) -> Result, EVMError> { + ) -> EVMResultGeneric, ChainSpecT, DB::Error> { let Some(outcome) = self.precompiles .call(address, input_data, gas.limit(), &mut self.inner) @@ -147,7 +127,7 @@ impl EvmContext { pub fn make_call_frame( &mut self, inputs: &CallInputs, - ) -> Result> { + ) -> EVMResultGeneric { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { @@ -169,7 +149,8 @@ impl EvmContext { let (account, _) = self .inner .journaled_state - .load_code(inputs.bytecode_address, &mut self.inner.db)?; + .load_code(inputs.bytecode_address, &mut self.inner.db) + .map_err(EVMError::Database)?; let code_hash = account.info.code_hash(); let bytecode = account.info.code.clone().unwrap_or_default(); @@ -180,17 +161,23 @@ impl EvmContext { match inputs.value { // if transfer value is zero, do the touch. CallValue::Transfer(value) if value == U256::ZERO => { - self.load_account(inputs.target_address)?; + self.load_account(inputs.target_address) + .map_err(EVMError::Database)?; self.journaled_state.touch(&inputs.target_address); } CallValue::Transfer(value) => { // Transfer value from caller to called account - if let Some(result) = self.inner.journaled_state.transfer( - &inputs.caller, - &inputs.target_address, - value, - &mut self.inner.db, - )? { + if let Some(result) = self + .inner + .journaled_state + .transfer( + &inputs.caller, + &inputs.target_address, + value, + &mut self.inner.db, + ) + .map_err(EVMError::Database)? + { self.journaled_state.checkpoint_revert(checkpoint); return return_result(result); } @@ -256,11 +243,11 @@ pub(crate) mod test_utils { /// Creates an evm context with a cache db backend. /// Additionally loads the mock caller account into the db, /// and sets the balance to the provided U256 value. - pub fn create_cache_db_evm_context_with_balance( - env: Box, + pub fn create_cache_db_evm_context_with_balance( + env: Box>, mut db: CacheDB, balance: U256, - ) -> EvmContext> { + ) -> EvmContext> { db.insert_account_info( test_utils::MOCK_CALLER, crate::primitives::AccountInfo { @@ -274,33 +261,32 @@ pub(crate) mod test_utils { } /// Creates a cached db evm context. - pub fn create_cache_db_evm_context( - env: Box, + pub fn create_cache_db_evm_context( + env: Box>, db: CacheDB, - ) -> EvmContext> { + ) -> EvmContext> { EvmContext { inner: InnerEvmContext { env, journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: Ok(()), - #[cfg(feature = "optimism")] - l1_block_info: None, }, precompiles: ContextPrecompiles::default(), } } /// Returns a new `EvmContext` with an empty journaled state. - pub fn create_empty_evm_context(env: Box, db: EmptyDB) -> EvmContext { + pub fn create_empty_evm_context( + env: Box>, + db: EmptyDB, + ) -> EvmContext { EvmContext { inner: InnerEvmContext { env, journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: Ok(()), - #[cfg(feature = "optimism")] - l1_block_info: None, }, precompiles: ContextPrecompiles::default(), } @@ -312,7 +298,7 @@ mod tests { use super::*; use crate::{ db::{CacheDB, EmptyDB}, - primitives::{address, Bytecode}, + primitives::{address, Bytecode, EthChainSpec}, Frame, JournalEntry, }; use std::boxed::Box; @@ -322,7 +308,7 @@ mod tests { // call stack is too deep. #[test] fn test_make_call_frame_stack_too_deep() { - let env = Env::default(); + let env = Env::::default(); let db = EmptyDB::default(); let mut context = test_utils::create_empty_evm_context(Box::new(env), db); context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1; @@ -343,7 +329,7 @@ mod tests { // checkpointed on the journaled state correctly. #[test] fn test_make_call_frame_transfer_revert() { - let env = Env::default(); + let env = Env::::default(); let db = EmptyDB::default(); let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); let contract = address!("dead10000000000000000000000000000001dead"); @@ -364,7 +350,7 @@ mod tests { #[test] fn test_make_call_frame_missing_code_context() { - let env = Env::default(); + let env = Env::::default(); let cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); let mut context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); @@ -379,7 +365,7 @@ mod tests { #[test] fn test_make_call_frame_succeeds() { - let env = Env::default(); + let env = Env::::default(); let mut cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00])); diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs index 32c2826a05..5ea93a2fb8 100644 --- a/crates/revm/src/context/inner_evm_context.rs +++ b/crates/revm/src/context/inner_evm_context.rs @@ -1,3 +1,5 @@ +use derive_where::derive_where; + use crate::{ db::Database, interpreter::{ @@ -8,70 +10,53 @@ use crate::{ }, journaled_state::JournaledState, primitives::{ - keccak256, AccessListItem, Account, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, - EVMError, Env, Eof, HashSet, Spec, + keccak256, AccessListItem, Account, Address, AnalysisKind, Bytecode, Bytes, ChainSpec, + CreateScheme, Env, Eof, HashSet, Spec, SpecId::{self, *}, - B256, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, U256, + Transaction as _, B256, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, U256, }, FrameOrResult, JournalCheckpoint, CALL_STACK_LIMIT, }; use std::{boxed::Box, sync::Arc}; /// EVM contexts contains data that EVM needs for execution. -#[derive(Debug)] -pub struct InnerEvmContext { +#[derive_where(Clone, Debug; ChainSpecT::Block, ChainSpecT::Transaction, DB, DB::Error)] +pub struct InnerEvmContext { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. - pub env: Box, + pub env: Box>, /// EVM State with journaling support. pub journaled_state: JournaledState, /// Database to load data from. pub db: DB, /// Error that happened during execution. - pub error: Result<(), EVMError>, - /// Used as temporary value holder to store L1 block info. - #[cfg(feature = "optimism")] - pub l1_block_info: Option, + pub error: Result<(), DB::Error>, } -impl Clone for InnerEvmContext +impl InnerEvmContext where - DB::Error: Clone, + ChainSpecT: ChainSpec, + DB: Database, { - fn clone(&self) -> Self { - Self { - env: self.env.clone(), - journaled_state: self.journaled_state.clone(), - db: self.db.clone(), - error: self.error.clone(), - #[cfg(feature = "optimism")] - l1_block_info: self.l1_block_info.clone(), - } - } -} - -impl InnerEvmContext { pub fn new(db: DB) -> Self { Self { env: Box::default(), journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), db, error: Ok(()), - #[cfg(feature = "optimism")] - l1_block_info: None, } } +} +impl InnerEvmContext { /// Creates a new context with the given environment and database. #[inline] - pub fn new_with_env(db: DB, env: Box) -> Self { + pub fn new_with_env(db: DB, env: Box>) -> Self { Self { env, journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), db, error: Ok(()), - #[cfg(feature = "optimism")] - l1_block_info: None, } } @@ -79,14 +64,12 @@ impl InnerEvmContext { /// /// Note that this will ignore the previous `error` if set. #[inline] - pub fn with_db(self, db: ODB) -> InnerEvmContext { + pub fn with_db(self, db: ODB) -> InnerEvmContext { InnerEvmContext { env: self.env, journaled_state: self.journaled_state, db, error: Ok(()), - #[cfg(feature = "optimism")] - l1_block_info: self.l1_block_info, } } @@ -100,11 +83,11 @@ impl InnerEvmContext { /// /// Loading of accounts/storages is needed to make them warm. #[inline] - pub fn load_access_list(&mut self) -> Result<(), EVMError> { + pub fn load_access_list(&mut self) -> Result<(), DB::Error> { for AccessListItem { address, storage_keys, - } in self.env.tx.access_list.iter() + } in self.env.tx.access_list() { self.journaled_state.initial_account_load( *address, @@ -117,20 +100,20 @@ impl InnerEvmContext { /// Return environment. #[inline] - pub fn env(&mut self) -> &mut Env { + pub fn env(&mut self) -> &mut Env { &mut self.env } /// Returns the error by replacing it with `Ok(())`, if any. #[inline] - pub fn take_error(&mut self) -> Result<(), EVMError> { + pub fn take_error(&mut self) -> Result<(), DB::Error> { core::mem::replace(&mut self.error, Ok(())) } /// Fetch block hash from database. #[inline] - pub fn block_hash(&mut self, number: u64) -> Result> { - self.db.block_hash(number).map_err(EVMError::Database) + pub fn block_hash(&mut self, number: u64) -> Result { + self.db.block_hash(number) } /// Mark account as touched as only touched accounts will be added to state. @@ -141,10 +124,7 @@ impl InnerEvmContext { /// Loads an account into memory. Returns `true` if it is cold accessed. #[inline] - pub fn load_account( - &mut self, - address: Address, - ) -> Result<(&mut Account, bool), EVMError> { + pub fn load_account(&mut self, address: Address) -> Result<(&mut Account, bool), DB::Error> { self.journaled_state.load_account(address, &mut self.db) } @@ -152,17 +132,14 @@ impl InnerEvmContext { /// /// Return boolean pair where first is `is_cold` second bool `exists`. #[inline] - pub fn load_account_exist( - &mut self, - address: Address, - ) -> Result> { + pub fn load_account_exist(&mut self, address: Address) -> Result { self.journaled_state .load_account_exist(address, &mut self.db) } /// Return account balance and is_cold flag. #[inline] - pub fn balance(&mut self, address: Address) -> Result<(U256, bool), EVMError> { + pub fn balance(&mut self, address: Address) -> Result<(U256, bool), DB::Error> { self.journaled_state .load_account(address, &mut self.db) .map(|(acc, is_cold)| (acc.info.balance, is_cold)) @@ -172,7 +149,7 @@ impl InnerEvmContext { /// /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. #[inline] - pub fn code(&mut self, address: Address) -> Result<(Bytes, bool), EVMError> { + pub fn code(&mut self, address: Address) -> Result<(Bytes, bool), DB::Error> { self.journaled_state .load_code(address, &mut self.db) .map(|(a, is_cold)| { @@ -191,7 +168,7 @@ impl InnerEvmContext { /// In case of EOF account it will return `EOF_MAGIC_HASH` /// (the hash of `0xEF00`). #[inline] - pub fn code_hash(&mut self, address: Address) -> Result<(B256, bool), EVMError> { + pub fn code_hash(&mut self, address: Address) -> Result<(B256, bool), DB::Error> { let (acc, is_cold) = self.journaled_state.load_code(address, &mut self.db)?; if acc.is_empty() { return Ok((B256::ZERO, is_cold)); @@ -204,11 +181,7 @@ impl InnerEvmContext { /// Load storage slot, if storage is not present inside the account then it will be loaded from database. #[inline] - pub fn sload( - &mut self, - address: Address, - index: U256, - ) -> Result<(U256, bool), EVMError> { + pub fn sload(&mut self, address: Address, index: U256) -> Result<(U256, bool), DB::Error> { // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` self.journaled_state.sload(address, index, &mut self.db) } @@ -220,7 +193,7 @@ impl InnerEvmContext { address: Address, index: U256, value: U256, - ) -> Result> { + ) -> Result { self.journaled_state .sstore(address, index, value, &mut self.db) } @@ -243,7 +216,7 @@ impl InnerEvmContext { &mut self, address: Address, target: Address, - ) -> Result> { + ) -> Result { self.journaled_state .selfdestruct(address, target, &mut self.db) } @@ -254,7 +227,7 @@ impl InnerEvmContext { &mut self, spec_id: SpecId, inputs: &EOFCreateInputs, - ) -> Result> { + ) -> Result { let return_error = |e| { Ok(FrameOrResult::new_eofcreate_result( InterpreterResult { @@ -273,15 +246,10 @@ impl InnerEvmContext { created_address, } => (input.clone(), initcode.clone(), *created_address), EOFCreateKind::Tx { initdata } => { - // Use nonce from tx (if set) or from account (if not). + // Use nonce from tx. // Nonce for call is bumped in deduct_caller // TODO(make this part of nonce increment code) - let nonce = self.env.tx.nonce.unwrap_or_else(|| { - let caller = self.env.tx.caller; - self.load_account(caller) - .map(|(a, _)| a.info.nonce) - .unwrap_or_default() - }); + let nonce = self.env.tx.nonce(); // decode eof and init code. let Ok((eof, input)) = Eof::decode_dangling(initdata.clone()) else { @@ -293,7 +261,7 @@ impl InnerEvmContext { return return_error(InstructionResult::InvalidEOFInitCode); } - (input, eof, self.env.tx.caller.create(nonce)) + (input, eof, self.env.tx.caller().create(nonce)) } }; @@ -405,7 +373,7 @@ impl InnerEvmContext { &mut self, spec_id: SpecId, inputs: &CreateInputs, - ) -> Result> { + ) -> Result { let return_error = |e| { Ok(FrameOrResult::new_create_result( InterpreterResult { diff --git a/crates/revm/src/custom_upcode.rs b/crates/revm/src/custom_upcode.rs new file mode 100644 index 0000000000..4cdb5b2d43 --- /dev/null +++ b/crates/revm/src/custom_upcode.rs @@ -0,0 +1,260 @@ +use revm_interpreter::{ + gas, + opcode::{make_instruction_table, InstructionTable, InstructionTables}, + Host, Interpreter, +}; + +use crate::{ + handler::register::EvmHandler, + primitives::{db::Database, ChainSpec, HaltReason, Spec, SpecId}, +}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct CustomOpcodeChainSpec; + +impl ChainSpec for CustomOpcodeChainSpec { + type Hardfork = CustomOpcodeSpecId; + type HaltReason = HaltReason; +} + +/// Specification IDs for the optimism blockchain. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +pub enum CustomOpcodeSpecId { + FRONTIER = 0, + FRONTIER_THAWING = 1, + HOMESTEAD = 2, + DAO_FORK = 3, + TANGERINE = 4, + SPURIOUS_DRAGON = 5, + BYZANTIUM = 6, + CONSTANTINOPLE = 7, + PETERSBURG = 8, + ISTANBUL = 9, + MUIR_GLACIER = 10, + BERLIN = 11, + LONDON = 12, + ARROW_GLACIER = 13, + GRAY_GLACIER = 14, + MERGE = 15, + // Introduces the custom opcode in between the existing hardforks. + INTRODUCES_OPCODE = 16, + SHANGHAI = 17, + CANCUN = 18, + PRAGUE = 19, + #[default] + LATEST = u8::MAX, +} + +impl CustomOpcodeSpecId { + /// Returns `true` if the given specification ID is enabled in this spec. + #[inline] + pub const fn enabled(our: Self, other: Self) -> bool { + our as u8 >= other as u8 + } + + /// Returns `true` if the given specification ID is enabled in this spec. + #[inline] + pub const fn is_enabled_in(self, other: Self) -> bool { + Self::enabled(self, other) + } + + /// Converts the `CustomOpcodeSpecId` into an `SpecId`. + const fn into_eth_spec_id(self) -> SpecId { + match self { + Self::FRONTIER => SpecId::FRONTIER, + Self::FRONTIER_THAWING => SpecId::FRONTIER_THAWING, + Self::HOMESTEAD => SpecId::HOMESTEAD, + Self::DAO_FORK => SpecId::DAO_FORK, + Self::TANGERINE => SpecId::TANGERINE, + Self::SPURIOUS_DRAGON => SpecId::SPURIOUS_DRAGON, + Self::BYZANTIUM => SpecId::BYZANTIUM, + Self::CONSTANTINOPLE => SpecId::CONSTANTINOPLE, + Self::PETERSBURG => SpecId::PETERSBURG, + Self::ISTANBUL => SpecId::ISTANBUL, + Self::MUIR_GLACIER => SpecId::MUIR_GLACIER, + Self::BERLIN => SpecId::BERLIN, + Self::LONDON => SpecId::LONDON, + Self::ARROW_GLACIER => SpecId::ARROW_GLACIER, + Self::GRAY_GLACIER => SpecId::GRAY_GLACIER, + Self::MERGE | Self::INTRODUCES_OPCODE => SpecId::MERGE, + Self::SHANGHAI => SpecId::SHANGHAI, + Self::CANCUN => SpecId::CANCUN, + Self::PRAGUE => SpecId::PRAGUE, + Self::LATEST => SpecId::LATEST, + } + } +} + +impl From for SpecId { + fn from(spec_id: CustomOpcodeSpecId) -> Self { + spec_id.into_eth_spec_id() + } +} + +pub trait CustomOpcodeSpec: Spec + Sized + 'static { + /// The specification ID for an imaginary chain with custom opcodes. + const CUSTOM_OPCODE_SPEC_ID: CustomOpcodeSpecId; + + /// Returns whether the provided `CustomOpcodeSpec` is enabled by this spec. + #[inline] + fn optimism_enabled(spec_id: CustomOpcodeSpecId) -> bool { + CustomOpcodeSpecId::enabled(Self::CUSTOM_OPCODE_SPEC_ID, spec_id) + } +} + +macro_rules! spec { + ($spec_id:ident, $spec_name:ident) => { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $spec_name; + + impl CustomOpcodeSpec for $spec_name { + const CUSTOM_OPCODE_SPEC_ID: CustomOpcodeSpecId = CustomOpcodeSpecId::$spec_id; + } + + impl Spec for $spec_name { + const SPEC_ID: SpecId = $spec_name::CUSTOM_OPCODE_SPEC_ID.into_eth_spec_id(); + } + }; +} + +spec!(FRONTIER, FrontierSpec); +// FRONTIER_THAWING no EVM spec change +spec!(HOMESTEAD, HomesteadSpec); +// DAO_FORK no EVM spec change +spec!(TANGERINE, TangerineSpec); +spec!(SPURIOUS_DRAGON, SpuriousDragonSpec); +spec!(BYZANTIUM, ByzantiumSpec); +// CONSTANTINOPLE was overridden with PETERSBURG +spec!(PETERSBURG, PetersburgSpec); +spec!(ISTANBUL, IstanbulSpec); +// MUIR_GLACIER no EVM spec change +spec!(BERLIN, BerlinSpec); +spec!(LONDON, LondonSpec); +// ARROW_GLACIER no EVM spec change +// GRAY_GLACIER no EVM spec change +spec!(MERGE, MergeSpec); +spec!(SHANGHAI, ShanghaiSpec); +spec!(CANCUN, CancunSpec); +spec!(PRAGUE, PragueSpec); + +spec!(LATEST, LatestSpec); + +// Custom Hardforks +spec!(INTRODUCES_OPCODE, IntroducesOpcodeSpec); + +macro_rules! custom_opcode_spec_to_generic { + ($spec_id:expr, $e:expr) => {{ + // We are transitioning from var to generic spec. + match $spec_id { + CustomOpcodeSpecId::FRONTIER | CustomOpcodeSpecId::FRONTIER_THAWING => { + use FrontierSpec as SPEC; + $e + } + CustomOpcodeSpecId::HOMESTEAD | CustomOpcodeSpecId::DAO_FORK => { + use HomesteadSpec as SPEC; + $e + } + CustomOpcodeSpecId::TANGERINE => { + use TangerineSpec as SPEC; + $e + } + CustomOpcodeSpecId::SPURIOUS_DRAGON => { + use SpuriousDragonSpec as SPEC; + $e + } + CustomOpcodeSpecId::BYZANTIUM => { + use ByzantiumSpec as SPEC; + $e + } + CustomOpcodeSpecId::PETERSBURG | CustomOpcodeSpecId::CONSTANTINOPLE => { + use PetersburgSpec as SPEC; + $e + } + CustomOpcodeSpecId::ISTANBUL | CustomOpcodeSpecId::MUIR_GLACIER => { + use IstanbulSpec as SPEC; + $e + } + CustomOpcodeSpecId::BERLIN => { + use BerlinSpec as SPEC; + $e + } + CustomOpcodeSpecId::LONDON + | CustomOpcodeSpecId::ARROW_GLACIER + | CustomOpcodeSpecId::GRAY_GLACIER => { + use LondonSpec as SPEC; + $e + } + CustomOpcodeSpecId::MERGE => { + use MergeSpec as SPEC; + $e + } + CustomOpcodeSpecId::SHANGHAI => { + use ShanghaiSpec as SPEC; + $e + } + CustomOpcodeSpecId::CANCUN => { + use CancunSpec as SPEC; + $e + } + CustomOpcodeSpecId::LATEST => { + use LatestSpec as SPEC; + $e + } + CustomOpcodeSpecId::PRAGUE => { + use PragueSpec as SPEC; + $e + } + CustomOpcodeSpecId::INTRODUCES_OPCODE => { + use IntroducesOpcodeSpec as SPEC; + $e + } + } + }}; +} + +impl EvmHandler<'_, CustomOpcodeChainSpec, EXT, DB> { + pub fn custom_opcode_with_spec(spec_id: CustomOpcodeSpecId) -> Self { + let mut handler = Self::mainnet_with_spec(spec_id); + + custom_opcode_spec_to_generic!(spec_id, { + let table = make_custom_instruction_table::<_, SPEC>(); + handler.set_instruction_table(InstructionTables::Plain(table)); + }); + + handler + } +} + +fn make_custom_instruction_table< + ChainSpecT: ChainSpec, + H: Host + ?Sized, + SPEC: CustomOpcodeSpec, +>() -> InstructionTable { + // custom opcode chain can reuse mainnet instructions + let mut table = make_instruction_table::(); + + table[0x0c] = custom_opcode_handler::; + + table +} + +fn custom_opcode_handler< + ChainSpecT: ChainSpec, + H: Host + ?Sized, + SPEC: CustomOpcodeSpec, +>( + interpreter: &mut Interpreter, + _host: &mut H, +) { + // opcode has access to the chain-specific spec + if SPEC::optimism_enabled(CustomOpcodeSpecId::INTRODUCES_OPCODE) { + gas!(interpreter, gas::MID); + } else { + gas!(interpreter, gas::HIGH); + } + + // logic +} diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 9c67a8e984..d476389b57 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,17 +1,17 @@ +use revm_interpreter::Host as _; + use crate::{ builder::{EvmBuilder, HandlerStage, SetGenericStage}, db::{Database, DatabaseCommit, EmptyDB}, - handler::Handler, - interpreter::{ - CallInputs, CreateInputs, EOFCreateInputs, Host, InterpreterAction, SharedMemory, - }, + handler::{EnvWithChainSpec, Handler}, + interpreter::{CallInputs, CreateInputs, EOFCreateInputs, InterpreterAction, SharedMemory}, primitives::{ - specification::SpecId, BlockEnv, CfgEnv, EVMError, EVMResult, EnvWithHandlerCfg, - ExecutionResult, HandlerCfg, ResultAndState, TxEnv, TxKind, + CfgEnv, ChainSpec, EVMError, EVMResult, EVMResultGeneric, EthChainSpec, ExecutionResult, + ResultAndState, SpecId, Transaction as _, TxKind, }, - Context, ContextWithHandlerCfg, Frame, FrameOrResult, FrameResult, + Context, ContextWithChainSpec, Frame, FrameOrResult, FrameResult, }; -use core::fmt; +use core::fmt::{self, Debug}; use std::{boxed::Box, vec::Vec}; /// EVM call stack limit. @@ -19,19 +19,19 @@ pub const CALL_STACK_LIMIT: u64 = 1024; /// EVM instance containing both internal EVM context and external context /// and the handler that dictates the logic of EVM (or hardfork specification). -pub struct Evm<'a, EXT, DB: Database> { +pub struct Evm<'a, ChainSpecT: ChainSpec, EXT, DB: Database> { /// Context of execution, containing both EVM and external context. - pub context: Context, + pub context: Context, /// Handler is a component of the of EVM that contains all the logic. Handler contains specification id /// and it different depending on the specified fork. - pub handler: Handler<'a, Context, EXT, DB>, + pub handler: Handler<'a, ChainSpecT, Context, EXT, DB>, } -impl fmt::Debug for Evm<'_, EXT, DB> +impl Debug for Evm<'_, ChainSpecT, EXT, DB> where - EXT: fmt::Debug, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, + ChainSpecT: ChainSpec, + EXT: Debug, + DB: Database + Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Evm") @@ -40,41 +40,49 @@ where } } -impl Evm<'_, EXT, DB> { +impl Evm<'_, ChainSpecT, EXT, DB> { /// Commit the changes to the database. - pub fn transact_commit(&mut self) -> Result> { + pub fn transact_commit( + &mut self, + ) -> EVMResultGeneric, ChainSpecT, DB::Error> { let ResultAndState { result, state } = self.transact()?; self.context.evm.db.commit(state); Ok(result) } } -impl<'a> Evm<'a, (), EmptyDB> { - /// Returns evm builder with empty database and empty external context. - pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { +impl<'a> Evm<'a, EthChainSpec, (), EmptyDB> { + /// Returns evm builder with the mainnet chain spec, empty database, and empty external context. + pub fn builder() -> EvmBuilder<'a, SetGenericStage, EthChainSpec, (), EmptyDB> { EvmBuilder::default() } } -impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> Evm<'a, ChainSpecT, EXT, DB> { /// Create new EVM. pub fn new( - mut context: Context, - handler: Handler<'a, Context, EXT, DB>, - ) -> Evm<'a, EXT, DB> { - context.evm.journaled_state.set_spec_id(handler.cfg.spec_id); + mut context: Context, + handler: Handler<'a, ChainSpecT, Context, EXT, DB>, + ) -> Evm<'a, ChainSpecT, EXT, DB> { + context + .evm + .journaled_state + .set_spec_id(handler.spec_id.into()); Evm { context, handler } } /// Allow for evm setting to be modified by feeding current evm /// into the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + pub fn modify(self) -> EvmBuilder<'a, HandlerStage, ChainSpecT, EXT, DB> { EvmBuilder::new(self) } /// Runs main call loop. #[inline] - pub fn run_the_loop(&mut self, first_frame: Frame) -> Result> { + pub fn run_the_loop( + &mut self, + first_frame: Frame, + ) -> EVMResultGeneric { let mut call_stack: Vec = Vec::with_capacity(1025); call_stack.push(first_frame); @@ -97,7 +105,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { // Take error and break the loop, if any. // This error can be set in the Interpreter when it interacts with the context. - self.context.evm.take_error()?; + self.context.evm.take_error().map_err(EVMError::Database)?; let exec = &mut self.handler.execution; let frame_or_result = match next_action { @@ -168,18 +176,18 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } -impl Evm<'_, EXT, DB> { +impl Evm<'_, ChainSpecT, EXT, DB> { /// Returns specification (hardfork) that the EVM is instanced with. /// /// SpecId depends on the handler. - pub fn spec_id(&self) -> SpecId { - self.handler.cfg.spec_id + pub fn spec_id(&self) -> ChainSpecT::Hardfork { + self.handler.spec_id } /// Pre verify transaction by checking Environment, initial gas spend and if caller /// has enough balance to pay for the gas. #[inline] - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { + pub fn preverify_transaction(&mut self) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { let output = self.preverify_transaction_inner().map(|_| ()); self.clear(); output @@ -194,7 +202,7 @@ impl Evm<'_, EXT, DB> { /// /// This function will not validate the transaction. #[inline] - pub fn transact_preverified(&mut self) -> EVMResult { + pub fn transact_preverified(&mut self) -> EVMResult { let initial_gas_spend = self .handler .validation() @@ -211,7 +219,7 @@ impl Evm<'_, EXT, DB> { /// Pre verify transaction inner. #[inline] - fn preverify_transaction_inner(&mut self) -> Result> { + fn preverify_transaction_inner(&mut self) -> EVMResultGeneric { self.handler.validation().env(&self.context.evm.env)?; let initial_gas_spend = self .handler @@ -227,7 +235,7 @@ impl Evm<'_, EXT, DB> { /// /// This function will validate the transaction. #[inline] - pub fn transact(&mut self) -> EVMResult { + pub fn transact(&mut self) -> EVMResult { let initial_gas_spend = self.preverify_transaction_inner().map_err(|e| { self.clear(); e @@ -239,12 +247,6 @@ impl Evm<'_, EXT, DB> { output } - /// Returns the reference of handler configuration - #[inline] - pub fn handler_cfg(&self) -> &HandlerCfg { - &self.handler.cfg - } - /// Returns the reference of Env configuration #[inline] pub fn cfg(&self) -> &CfgEnv { @@ -259,13 +261,13 @@ impl Evm<'_, EXT, DB> { /// Returns the reference of transaction #[inline] - pub fn tx(&self) -> &TxEnv { + pub fn tx(&self) -> &ChainSpecT::Transaction { &self.context.evm.env.tx } /// Returns the mutable reference of transaction #[inline] - pub fn tx_mut(&mut self) -> &mut TxEnv { + pub fn tx_mut(&mut self) -> &mut ChainSpecT::Transaction { &mut self.context.evm.env.tx } @@ -283,47 +285,45 @@ impl Evm<'_, EXT, DB> { /// Returns the reference of block #[inline] - pub fn block(&self) -> &BlockEnv { + pub fn block(&self) -> &ChainSpecT::Block { &self.context.evm.env.block } /// Returns the mutable reference of block #[inline] - pub fn block_mut(&mut self) -> &mut BlockEnv { + pub fn block_mut(&mut self) -> &mut ChainSpecT::Block { &mut self.context.evm.env.block } - /// Modify spec id, this will create new EVM that matches this spec id. - pub fn modify_spec_id(&mut self, spec_id: SpecId) { - self.handler.modify_spec_id(spec_id); - } - /// Returns internal database and external struct. #[inline] - pub fn into_context(self) -> Context { + pub fn into_context(self) -> Context { self.context } - /// Returns database and [`EnvWithHandlerCfg`]. + /// Returns database and [`EnvWithChainSpec`]. #[inline] - pub fn into_db_and_env_with_handler_cfg(self) -> (DB, EnvWithHandlerCfg) { + pub fn into_db_and_env_with_handler_cfg(self) -> (DB, EnvWithChainSpec) { ( self.context.evm.inner.db, - EnvWithHandlerCfg { + EnvWithChainSpec { env: self.context.evm.inner.env, - handler_cfg: self.handler.cfg, + spec_id: self.handler.spec_id, }, ) } - /// Returns [Context] and [HandlerCfg]. + /// Returns [Context] and hardfork. #[inline] - pub fn into_context_with_handler_cfg(self) -> ContextWithHandlerCfg { - ContextWithHandlerCfg::new(self.context, self.handler.cfg) + pub fn into_context_with_spec_id(self) -> ContextWithChainSpec { + ContextWithChainSpec::new(self.context, self.handler.spec_id) } /// Transact pre-verified transaction. - fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + fn transact_preverified_inner( + &mut self, + initial_gas_spend: u64, + ) -> EVMResult { let spec_id = self.spec_id(); let ctx = &mut self.context; let pre_exec = self.handler.pre_execution(); @@ -338,29 +338,32 @@ impl Evm<'_, EXT, DB> { // deduce caller balance with its limit. pre_exec.deduct_caller(ctx)?; - let gas_limit = ctx.evm.env.tx.gas_limit - initial_gas_spend; + let gas_limit = ctx.evm.env.tx.gas_limit() - initial_gas_spend; let exec = self.handler.execution(); // call inner handling of call/create - let first_frame_or_result = match ctx.evm.env.tx.transact_to { + let first_frame_or_result = match ctx.evm.env.tx.kind() { TxKind::Call(_) => exec.call( ctx, CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), )?, TxKind::Create => { // if first byte of data is magic 0xEF00, then it is EOFCreate. - if spec_id.is_enabled_in(SpecId::PRAGUE_EOF) + if Into::::into(spec_id).is_enabled_in(SpecId::PRAGUE_EOF) && ctx .env() .tx - .data + .data() .get(0..2) .filter(|&t| t == [0xEF, 00]) .is_some() { exec.eofcreate( ctx, - Box::new(EOFCreateInputs::new_tx(&ctx.evm.env.tx, gas_limit)), + Box::new(EOFCreateInputs::new_tx::( + &ctx.evm.env.tx, + gas_limit, + )), )? } else { // Safe to unwrap because we are sure that it is create tx. diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index fab20b1229..6c9a691b98 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,15 +1,20 @@ // Modules. +pub mod cfg; mod handle_types; pub mod mainnet; pub mod register; // Exports. +pub use cfg::{CfgEnvWithChainSpec, EnvWithChainSpec}; pub use handle_types::*; // Includes. use crate::{ interpreter::{opcode::InstructionTables, Host, InterpreterAction, SharedMemory}, - primitives::{db::Database, spec_to_generic, EVMError, HandlerCfg, Spec, SpecId}, + primitives::{ + db::Database, spec_to_generic, ChainSpec, EVMResultGeneric, InvalidTransaction, + TransactionValidation, + }, Context, Frame, }; use core::mem; @@ -21,91 +26,54 @@ use self::register::{HandleRegister, HandleRegisterBox}; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { - /// Handler configuration. - pub cfg: HandlerCfg, +pub struct Handler<'a, ChainSpecT: ChainSpec, H: Host + 'a, EXT, DB: Database> { + /// Handler hardfork + pub spec_id: ChainSpecT::Hardfork, /// Instruction table type. pub instruction_table: InstructionTables<'a, H>, /// Registers that will be called on initialization. - pub registers: Vec>, + pub registers: Vec>, /// Validity handles. - pub validation: ValidationHandler<'a, EXT, DB>, + pub validation: ValidationHandler<'a, ChainSpecT, EXT, DB>, /// Pre execution handle. - pub pre_execution: PreExecutionHandler<'a, EXT, DB>, + pub pre_execution: PreExecutionHandler<'a, ChainSpecT, EXT, DB>, /// Post Execution handle. - pub post_execution: PostExecutionHandler<'a, EXT, DB>, + pub post_execution: PostExecutionHandler<'a, ChainSpecT, EXT, DB>, /// Execution loop that handles frames. - pub execution: ExecutionHandler<'a, EXT, DB>, + pub execution: ExecutionHandler<'a, ChainSpecT, EXT, DB>, } -impl<'a, EXT, DB: Database> EvmHandler<'a, EXT, DB> { - /// Created new Handler with given configuration. - /// - /// Internally it calls `mainnet_with_spec` with the given spec id. - /// Or `optimism_with_spec` if the optimism feature is enabled and `cfg.is_optimism` is set. - pub fn new(cfg: HandlerCfg) -> Self { - cfg_if::cfg_if! { - if #[cfg(feature = "optimism")] { - if cfg.is_optimism { - Handler::optimism_with_spec(cfg.spec_id) - } else { - Handler::mainnet_with_spec(cfg.spec_id) - } - } else { - Handler::mainnet_with_spec(cfg.spec_id) +impl<'a, ChainSpecT, EXT, DB> EvmHandler<'a, ChainSpecT, EXT, DB> +where + ChainSpecT: + ChainSpec>>, + DB: Database, +{ + /// Creates a base/vanilla Ethereum handler with the provided spec id. + pub fn mainnet_with_spec(spec_id: ChainSpecT::Hardfork) -> Self { + spec_to_generic!( + spec_id.into(), + Self { + spec_id, + instruction_table: InstructionTables::new_plain::(), + registers: Vec::new(), + validation: ValidationHandler::new::(), + pre_execution: PreExecutionHandler::new::(), + post_execution: PostExecutionHandler::mainnet::(), + execution: ExecutionHandler::new::(), } - } - } - - /// Default handler for Ethereum mainnet. - pub fn mainnet() -> Self { - Self { - cfg: HandlerCfg::new(SPEC::SPEC_ID), - instruction_table: InstructionTables::new_plain::(), - registers: Vec::new(), - validation: ValidationHandler::new::(), - pre_execution: PreExecutionHandler::new::(), - post_execution: PostExecutionHandler::new::(), - execution: ExecutionHandler::new::(), - } - } - - /// Returns `true` if the optimism feature is enabled and flag is set to `true`. - pub fn is_optimism(&self) -> bool { - self.cfg.is_optimism() - } - - /// Handler for optimism - #[cfg(feature = "optimism")] - pub fn optimism() -> Self { - let mut handler = Self::mainnet::(); - handler.cfg.is_optimism = true; - handler.append_handler_register(HandleRegisters::Plain( - crate::optimism::optimism_handle_register::, - )); - handler - } - - /// Optimism with spec. Similar to [`Self::mainnet_with_spec`]. - #[cfg(feature = "optimism")] - pub fn optimism_with_spec(spec_id: SpecId) -> Self { - spec_to_generic!(spec_id, Self::optimism::()) - } - - /// Creates handler with variable spec id, inside it will call `mainnet::` for - /// appropriate spec. - pub fn mainnet_with_spec(spec_id: SpecId) -> Self { - spec_to_generic!(spec_id, Self::mainnet::()) - } - - /// Specification ID. - pub fn cfg(&self) -> HandlerCfg { - self.cfg + ) } +} - /// Returns specification ID. - pub fn spec_id(&self) -> SpecId { - self.cfg.spec_id +impl<'a, ChainSpecT, EXT, DB> EvmHandler<'a, ChainSpecT, EXT, DB> +where + ChainSpecT: ChainSpec, + DB: Database, +{ + /// Returns the specification ID. + pub fn spec_id(&self) -> ChainSpecT::Hardfork { + self.spec_id } /// Executes call frame. @@ -113,71 +81,86 @@ impl<'a, EXT, DB: Database> EvmHandler<'a, EXT, DB> { &self, frame: &mut Frame, shared_memory: &mut SharedMemory, - context: &mut Context, - ) -> Result> { + context: &mut Context, + ) -> EVMResultGeneric { self.execution .execute_frame(frame, shared_memory, &self.instruction_table, context) } /// Take instruction table. - pub fn take_instruction_table(&mut self) -> InstructionTables<'a, Context> { + pub fn take_instruction_table( + &mut self, + ) -> InstructionTables<'a, Context> { let spec_id = self.spec_id(); mem::replace( &mut self.instruction_table, - spec_to_generic!(spec_id, InstructionTables::new_plain::()), + spec_to_generic!(spec_id.into(), InstructionTables::new_plain::()), ) } /// Set instruction table. - pub fn set_instruction_table(&mut self, table: InstructionTables<'a, Context>) { + pub fn set_instruction_table( + &mut self, + table: InstructionTables<'a, Context>, + ) { self.instruction_table = table; } /// Returns reference to pre execution handler. - pub fn pre_execution(&self) -> &PreExecutionHandler<'a, EXT, DB> { + pub fn pre_execution(&self) -> &PreExecutionHandler<'a, ChainSpecT, EXT, DB> { &self.pre_execution } /// Returns reference to pre execution handler. - pub fn post_execution(&self) -> &PostExecutionHandler<'a, EXT, DB> { + pub fn post_execution(&self) -> &PostExecutionHandler<'a, ChainSpecT, EXT, DB> { &self.post_execution } /// Returns reference to frame handler. - pub fn execution(&self) -> &ExecutionHandler<'a, EXT, DB> { + pub fn execution(&self) -> &ExecutionHandler<'a, ChainSpecT, EXT, DB> { &self.execution } /// Returns reference to validation handler. - pub fn validation(&self) -> &ValidationHandler<'a, EXT, DB> { + pub fn validation(&self) -> &ValidationHandler<'a, ChainSpecT, EXT, DB> { &self.validation } /// Append handle register. - pub fn append_handler_register(&mut self, register: HandleRegisters) { + pub fn append_handler_register(&mut self, register: HandleRegisters) { register.register(self); self.registers.push(register); } /// Append plain handle register. - pub fn append_handler_register_plain(&mut self, register: HandleRegister) { + pub fn append_handler_register_plain(&mut self, register: HandleRegister) { register(self); self.registers.push(HandleRegisters::Plain(register)); } /// Append boxed handle register. - pub fn append_handler_register_box(&mut self, register: HandleRegisterBox) { + pub fn append_handler_register_box( + &mut self, + register: HandleRegisterBox, + ) { register(self); self.registers.push(HandleRegisters::Box(register)); } +} +impl<'a, ChainSpecT, EXT, DB> EvmHandler<'a, ChainSpecT, EXT, DB> +where + ChainSpecT: + ChainSpec>>, + DB: Database, +{ /// Pop last handle register and reapply all registers that are left. - pub fn pop_handle_register(&mut self) -> Option> { + pub fn pop_handle_register(&mut self) -> Option> { let out = self.registers.pop(); if out.is_some() { let registers = core::mem::take(&mut self.registers); - let mut base_handler = Handler::mainnet_with_spec(self.cfg.spec_id); - // apply all registers to default handeler and raw mainnet instruction table. + let mut base_handler = Handler::mainnet_with_spec(self.spec_id); + // apply all registers to default handler and raw mainnet instruction table. for register in registers { base_handler.append_handler_register(register) } @@ -186,20 +169,9 @@ impl<'a, EXT, DB: Database> EvmHandler<'a, EXT, DB> { out } - /// Creates the Handler with Generic Spec. - pub fn create_handle_generic(&mut self) -> EvmHandler<'a, EXT, DB> { - let registers = core::mem::take(&mut self.registers); - let mut base_handler = Handler::mainnet::(); - // apply all registers to default handeler and raw mainnet instruction table. - for register in registers { - base_handler.append_handler_register(register) - } - base_handler - } - /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. - pub fn modify_spec_id(&mut self, spec_id: SpecId) { - if self.cfg.spec_id == spec_id { + pub fn modify_spec_id(&mut self, spec_id: ChainSpecT::Hardfork) { + if self.spec_id == spec_id { return; } @@ -210,8 +182,7 @@ impl<'a, EXT, DB: Database> EvmHandler<'a, EXT, DB> { for register in registers { handler.append_handler_register(register) } - handler.cfg = self.cfg(); - handler.cfg.spec_id = spec_id; + handler.spec_id = spec_id; *self = handler; } } @@ -225,9 +196,14 @@ mod test { use super::*; + #[cfg(feature = "optimism")] + type TestChainSpec = crate::optimism::OptimismChainSpec; + #[cfg(not(feature = "optimism"))] + type TestChainSpec = crate::primitives::EthChainSpec; + #[test] fn test_handler_register_pop() { - let register = |inner: &Rc>| -> HandleRegisterBox<(), EmptyDB> { + let register = |inner: &Rc>| -> HandleRegisterBox { let inner = inner.clone(); Box::new(move |h| { *inner.borrow_mut() += 1; @@ -235,7 +211,9 @@ mod test { }) }; - let mut handler = EvmHandler::<(), EmptyDB>::new(HandlerCfg::new(SpecId::LATEST)); + let mut handler = EvmHandler::<'_, TestChainSpec, (), EmptyDB>::mainnet_with_spec( + ::Hardfork::default(), + ); let test = Rc::new(RefCell::new(0)); handler.append_handler_register_box(register(&test)); diff --git a/crates/revm/src/handler/cfg.rs b/crates/revm/src/handler/cfg.rs new file mode 100644 index 0000000000..bc64c24724 --- /dev/null +++ b/crates/revm/src/handler/cfg.rs @@ -0,0 +1,96 @@ +use core::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; +use std::boxed::Box; + +use derive_where::derive_where; + +use crate::primitives::{CfgEnv, ChainSpec, Env}; + +/// Configuration environment with the chain spec id. +#[derive(Debug, Eq, PartialEq)] +#[derive_where(Clone; ChainSpecT::Hardfork)] +pub struct CfgEnvWithChainSpec { + /// Configuration environment. + pub cfg_env: CfgEnv, + /// Handler configuration fields. + pub spec_id: ChainSpecT::Hardfork, +} + +impl CfgEnvWithChainSpec { + /// Returns new instance of `CfgEnvWithHandlerCfg`. + pub fn new(cfg_env: CfgEnv, spec_id: ChainSpecT::Hardfork) -> Self { + Self { cfg_env, spec_id } + } +} + +impl DerefMut for CfgEnvWithChainSpec { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cfg_env + } +} + +impl Deref for CfgEnvWithChainSpec { + type Target = CfgEnv; + + fn deref(&self) -> &Self::Target { + &self.cfg_env + } +} + +/// Evm environment with the chain spec id. +#[derive_where(Clone, Debug; ChainSpecT::Block, ChainSpecT::Hardfork, ChainSpecT::Transaction)] +pub struct EnvWithChainSpec +where + ChainSpecT: ChainSpec, +{ + /// Evm enironment. + pub env: Box>, + /// Handler configuration fields. + pub spec_id: ChainSpecT::Hardfork, +} + +impl EnvWithChainSpec +where + ChainSpecT: ChainSpec, +{ + /// Returns new `EnvWithHandlerCfg` instance. + pub fn new(env: Box>, spec_id: ChainSpecT::Hardfork) -> Self { + Self { env, spec_id } + } + + /// Takes `CfgEnvWithHandlerCfg` and returns new `EnvWithHandlerCfg` instance. + pub fn new_with_cfg_env( + cfg: CfgEnvWithChainSpec, + block: ChainSpecT::Block, + tx: ChainSpecT::Transaction, + ) -> Self { + Self::new(Env::boxed(cfg.cfg_env, block, tx), cfg.spec_id) + } + + /// Returns the specification id. + pub const fn spec_id(&self) -> ChainSpecT::Hardfork { + self.spec_id + } +} + +impl DerefMut for EnvWithChainSpec +where + ChainSpecT: ChainSpec, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.env + } +} + +impl Deref for EnvWithChainSpec +where + ChainSpecT: ChainSpec, +{ + type Target = Env; + + fn deref(&self) -> &Self::Target { + &self.env + } +} diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs index 27d31eb118..f5bd8cac3e 100644 --- a/crates/revm/src/handler/handle_types/execution.rs +++ b/crates/revm/src/handler/handle_types/execution.rs @@ -2,7 +2,7 @@ use crate::{ frame::EOFCreateFrame, handler::mainnet, interpreter::{CallInputs, CreateInputs, SharedMemory}, - primitives::{db::Database, EVMError, Spec}, + primitives::{db::Database, ChainSpec, EVMResultGeneric, Spec}, CallFrame, Context, CreateFrame, Frame, FrameOrResult, FrameResult, }; use revm_interpreter::{ @@ -12,166 +12,171 @@ use revm_interpreter::{ use std::{boxed::Box, sync::Arc}; /// Handles first frame return handle. -pub type LastFrameReturnHandle<'a, EXT, DB> = Arc< - dyn Fn(&mut Context, &mut FrameResult) -> Result<(), EVMError<::Error>> +pub type LastFrameReturnHandle<'a, ChainSpecT, EXT, DB> = Arc< + dyn Fn( + &mut Context, + &mut FrameResult, + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + 'a, >; /// Executes a single frame. Errors can be returned in the EVM context. -pub type ExecuteFrameHandle<'a, EXT, DB> = Arc< +pub type ExecuteFrameHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( &mut Frame, &mut SharedMemory, - &InstructionTables<'_, Context>, - &mut Context, - ) -> Result::Error>> + &InstructionTables<'_, Context>, + &mut Context, + ) -> EVMResultGeneric::Error> + 'a, >; /// Handle sub call. -pub type FrameCallHandle<'a, EXT, DB> = Arc< +pub type FrameCallHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Handle call return -pub type FrameCallReturnHandle<'a, EXT, DB> = Arc< +pub type FrameCallReturnHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, InterpreterResult, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Insert call outcome to the parent -pub type InsertCallOutcomeHandle<'a, EXT, DB> = Arc< +pub type InsertCallOutcomeHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, &mut Frame, &mut SharedMemory, CallOutcome, - ) -> Result<(), EVMError<::Error>> + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + 'a, >; /// Handle sub create. -pub type FrameCreateHandle<'a, EXT, DB> = Arc< +pub type FrameCreateHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Handle create return -pub type FrameCreateReturnHandle<'a, EXT, DB> = Arc< +pub type FrameCreateReturnHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, InterpreterResult, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Insert call outcome to the parent -pub type InsertCreateOutcomeHandle<'a, EXT, DB> = Arc< +pub type InsertCreateOutcomeHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, &mut Frame, CreateOutcome, - ) -> Result<(), EVMError<::Error>> + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + 'a, >; /// Handle EOF sub create. -pub type FrameEOFCreateHandle<'a, EXT, DB> = Arc< +pub type FrameEOFCreateHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Handle EOF create return -pub type FrameEOFCreateReturnHandle<'a, EXT, DB> = Arc< +pub type FrameEOFCreateReturnHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, Box, InterpreterResult, - ) -> Result::Error>> + ) -> EVMResultGeneric::Error> + 'a, >; /// Insert EOF crate outcome to the parent -pub type InsertEOFCreateOutcomeHandle<'a, EXT, DB> = Arc< +pub type InsertEOFCreateOutcomeHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, &mut Frame, CreateOutcome, - ) -> Result<(), EVMError<::Error>> + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + 'a, >; /// Handles related to stack frames. -pub struct ExecutionHandler<'a, EXT, DB: Database> { +pub struct ExecutionHandler<'a, ChainSpecT: ChainSpec, EXT, DB: Database> { /// Handles last frame return, modified gas for refund and /// sets tx gas limit. - pub last_frame_return: LastFrameReturnHandle<'a, EXT, DB>, + pub last_frame_return: LastFrameReturnHandle<'a, ChainSpecT, EXT, DB>, /// Executes a single frame. - pub execute_frame: ExecuteFrameHandle<'a, EXT, DB>, + pub execute_frame: ExecuteFrameHandle<'a, ChainSpecT, EXT, DB>, /// Frame call - pub call: FrameCallHandle<'a, EXT, DB>, + pub call: FrameCallHandle<'a, ChainSpecT, EXT, DB>, /// Call return - pub call_return: FrameCallReturnHandle<'a, EXT, DB>, + pub call_return: FrameCallReturnHandle<'a, ChainSpecT, EXT, DB>, /// Insert call outcome - pub insert_call_outcome: InsertCallOutcomeHandle<'a, EXT, DB>, + pub insert_call_outcome: InsertCallOutcomeHandle<'a, ChainSpecT, EXT, DB>, /// Frame crate - pub create: FrameCreateHandle<'a, EXT, DB>, + pub create: FrameCreateHandle<'a, ChainSpecT, EXT, DB>, /// Crate return - pub create_return: FrameCreateReturnHandle<'a, EXT, DB>, + pub create_return: FrameCreateReturnHandle<'a, ChainSpecT, EXT, DB>, /// Insert create outcome. - pub insert_create_outcome: InsertCreateOutcomeHandle<'a, EXT, DB>, + pub insert_create_outcome: InsertCreateOutcomeHandle<'a, ChainSpecT, EXT, DB>, /// Frame EOFCreate - pub eofcreate: FrameEOFCreateHandle<'a, EXT, DB>, + pub eofcreate: FrameEOFCreateHandle<'a, ChainSpecT, EXT, DB>, /// EOFCreate return - pub eofcreate_return: FrameEOFCreateReturnHandle<'a, EXT, DB>, + pub eofcreate_return: FrameEOFCreateReturnHandle<'a, ChainSpecT, EXT, DB>, /// Insert EOFCreate outcome. - pub insert_eofcreate_outcome: InsertEOFCreateOutcomeHandle<'a, EXT, DB>, + pub insert_eofcreate_outcome: InsertEOFCreateOutcomeHandle<'a, ChainSpecT, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> ExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT: 'a, DB: Database + 'a> + ExecutionHandler<'a, ChainSpecT, EXT, DB> +{ /// Creates mainnet ExecutionHandler. pub fn new() -> Self { Self { - last_frame_return: Arc::new(mainnet::last_frame_return::), - execute_frame: Arc::new(mainnet::execute_frame::), - call: Arc::new(mainnet::call::), - call_return: Arc::new(mainnet::call_return::), + last_frame_return: Arc::new(mainnet::last_frame_return::), + execute_frame: Arc::new(mainnet::execute_frame::), + call: Arc::new(mainnet::call::), + call_return: Arc::new(mainnet::call_return::), insert_call_outcome: Arc::new(mainnet::insert_call_outcome), - create: Arc::new(mainnet::create::), - create_return: Arc::new(mainnet::create_return::), + create: Arc::new(mainnet::create::), + create_return: Arc::new(mainnet::create_return::), insert_create_outcome: Arc::new(mainnet::insert_create_outcome), - eofcreate: Arc::new(mainnet::eofcreate::), - eofcreate_return: Arc::new(mainnet::eofcreate_return::), + eofcreate: Arc::new(mainnet::eofcreate::), + eofcreate_return: Arc::new(mainnet::eofcreate_return::), insert_eofcreate_outcome: Arc::new(mainnet::insert_eofcreate_outcome), } } } -impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> ExecutionHandler<'a, ChainSpecT, EXT, DB> { /// Executes single frame. #[inline] pub fn execute_frame( &self, frame: &mut Frame, shared_memory: &mut SharedMemory, - instruction_tables: &InstructionTables<'_, Context>, - context: &mut Context, - ) -> Result> { + instruction_tables: &InstructionTables<'_, Context>, + context: &mut Context, + ) -> EVMResultGeneric { (self.execute_frame)(frame, shared_memory, instruction_tables, context) } @@ -179,9 +184,9 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn last_frame_return( &self, - context: &mut Context, + context: &mut Context, frame_result: &mut FrameResult, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.last_frame_return)(context, frame_result) } @@ -189,9 +194,9 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn call( &self, - context: &mut Context, + context: &mut Context, inputs: Box, - ) -> Result> { + ) -> EVMResultGeneric { (self.call)(context, inputs) } @@ -199,10 +204,10 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn call_return( &self, - context: &mut Context, + context: &mut Context, frame: Box, interpreter_result: InterpreterResult, - ) -> Result> { + ) -> EVMResultGeneric { (self.call_return)(context, frame, interpreter_result) } @@ -210,11 +215,11 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn insert_call_outcome( &self, - context: &mut Context, + context: &mut Context, frame: &mut Frame, shared_memory: &mut SharedMemory, outcome: CallOutcome, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.insert_call_outcome)(context, frame, shared_memory, outcome) } @@ -222,9 +227,9 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn create( &self, - context: &mut Context, + context: &mut Context, inputs: Box, - ) -> Result> { + ) -> EVMResultGeneric { (self.create)(context, inputs) } @@ -232,10 +237,10 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn create_return( &self, - context: &mut Context, + context: &mut Context, frame: Box, interpreter_result: InterpreterResult, - ) -> Result> { + ) -> EVMResultGeneric { (self.create_return)(context, frame, interpreter_result) } @@ -243,10 +248,10 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn insert_create_outcome( &self, - context: &mut Context, + context: &mut Context, frame: &mut Frame, outcome: CreateOutcome, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.insert_create_outcome)(context, frame, outcome) } @@ -254,9 +259,9 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn eofcreate( &self, - context: &mut Context, + context: &mut Context, inputs: Box, - ) -> Result> { + ) -> EVMResultGeneric { (self.eofcreate)(context, inputs) } @@ -264,10 +269,10 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn eofcreate_return( &self, - context: &mut Context, + context: &mut Context, frame: Box, interpreter_result: InterpreterResult, - ) -> Result> { + ) -> EVMResultGeneric { (self.eofcreate_return)(context, frame, interpreter_result) } @@ -275,10 +280,10 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { #[inline] pub fn insert_eofcreate_outcome( &self, - context: &mut Context, + context: &mut Context, frame: &mut Frame, outcome: CreateOutcome, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.insert_eofcreate_outcome)(context, frame, outcome) } } diff --git a/crates/revm/src/handler/handle_types/post_execution.rs b/crates/revm/src/handler/handle_types/post_execution.rs index f2149e8903..6e67a4cb86 100644 --- a/crates/revm/src/handler/handle_types/post_execution.rs +++ b/crates/revm/src/handler/handle_types/post_execution.rs @@ -2,24 +2,31 @@ use crate::{ handler::mainnet, interpreter::Gas, - primitives::{db::Database, EVMError, EVMResultGeneric, ResultAndState, Spec}, + primitives::{db::Database, ChainSpec, EVMResultGeneric, ResultAndState, Spec}, Context, FrameResult, }; use std::sync::Arc; /// Reimburse the caller with ethereum it didn't spent. -pub type ReimburseCallerHandle<'a, EXT, DB> = - Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; +pub type ReimburseCallerHandle<'a, ChainSpecT, EXT, DB> = Arc< + dyn Fn( + &mut Context, + &Gas, + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + + 'a, +>; /// Reward beneficiary with transaction rewards. -pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; +pub type RewardBeneficiaryHandle<'a, ChainSpecT, EXT, DB> = + ReimburseCallerHandle<'a, ChainSpecT, EXT, DB>; /// Main return handle, takes state from journal and transforms internal result to external. -pub type OutputHandle<'a, EXT, DB> = Arc< +pub type OutputHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, + &mut Context, FrameResult, - ) -> Result::Error>> + ) + -> EVMResultGeneric, ChainSpecT, ::Error> + 'a, >; @@ -27,86 +34,89 @@ pub type OutputHandle<'a, EXT, DB> = Arc< /// This will be called after all the other handlers. /// /// It is useful for catching errors and returning them in a different way. -pub type EndHandle<'a, EXT, DB> = Arc< +pub type EndHandle<'a, ChainSpecT, EXT, DB> = Arc< dyn Fn( - &mut Context, - Result::Error>>, - ) -> Result::Error>> + &mut Context, + EVMResultGeneric, ChainSpecT, ::Error>, + ) + -> EVMResultGeneric, ChainSpecT, ::Error> + 'a, >; /// Clear handle, doesn't have output, its purpose is to clear the /// context. It will be always called even on failed validation. -pub type ClearHandle<'a, EXT, DB> = Arc) + 'a>; +pub type ClearHandle<'a, ChainSpecT, EXT, DB> = Arc) + 'a>; /// Handles related to post execution after the stack loop is finished. -pub struct PostExecutionHandler<'a, EXT, DB: Database> { +pub struct PostExecutionHandler<'a, ChainSpecT: ChainSpec, EXT, DB: Database> { /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, + pub reimburse_caller: ReimburseCallerHandle<'a, ChainSpecT, EXT, DB>, /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, + pub reward_beneficiary: RewardBeneficiaryHandle<'a, ChainSpecT, EXT, DB>, /// Main return handle, returns the output of the transact. - pub output: OutputHandle<'a, EXT, DB>, + pub output: OutputHandle<'a, ChainSpecT, EXT, DB>, /// Called when execution ends. /// End handle in comparison to output handle will be called every time after execution. /// Output in case of error will not be called. - pub end: EndHandle<'a, EXT, DB>, + pub end: EndHandle<'a, ChainSpecT, EXT, DB>, /// Clear handle will be called always. In comparison to end that /// is called only on execution end, clear handle is called even if validation fails. - pub clear: ClearHandle<'a, EXT, DB>, + pub clear: ClearHandle<'a, ChainSpecT, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT: 'a, DB: Database + 'a> + PostExecutionHandler<'a, ChainSpecT, EXT, DB> +{ /// Creates mainnet MainHandles. - pub fn new() -> Self { + pub fn mainnet() -> Self { Self { - reimburse_caller: Arc::new(mainnet::reimburse_caller::), - reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), - output: Arc::new(mainnet::output::), - end: Arc::new(mainnet::end::), - clear: Arc::new(mainnet::clear::), + reimburse_caller: Arc::new(mainnet::reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + output: Arc::new(mainnet::output::), + end: Arc::new(mainnet::end::), + clear: Arc::new(mainnet::clear::), } } } -impl<'a, EXT, DB: Database> PostExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> PostExecutionHandler<'a, ChainSpecT, EXT, DB> { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, - context: &mut Context, + context: &mut Context, gas: &Gas, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.reimburse_caller)(context, gas) } /// Reward beneficiary pub fn reward_beneficiary( &self, - context: &mut Context, + context: &mut Context, gas: &Gas, - ) -> Result<(), EVMError> { + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.reward_beneficiary)(context, gas) } /// Returns the output of transaction. pub fn output( &self, - context: &mut Context, + context: &mut Context, result: FrameResult, - ) -> Result> { + ) -> EVMResultGeneric, ChainSpecT, DB::Error> { (self.output)(context, result) } /// End handler. pub fn end( &self, - context: &mut Context, - end_output: Result>, - ) -> Result> { + context: &mut Context, + end_output: EVMResultGeneric, ChainSpecT, DB::Error>, + ) -> EVMResultGeneric, ChainSpecT, DB::Error> { (self.end)(context, end_output) } /// Clean handler. - pub fn clear(&self, context: &mut Context) { + pub fn clear(&self, context: &mut Context) { (self.clear)(context) } } diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs index 1956d67f13..40527752c0 100644 --- a/crates/revm/src/handler/handle_types/pre_execution.rs +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -1,58 +1,75 @@ // Includes. use crate::{ handler::mainnet, - primitives::{db::Database, EVMError, EVMResultGeneric, Spec}, + primitives::{db::Database, ChainSpec, EVMResultGeneric, Spec}, Context, ContextPrecompiles, }; use std::sync::Arc; /// Loads precompiles into Evm -pub type LoadPrecompilesHandle<'a, DB> = Arc ContextPrecompiles + 'a>; +pub type LoadPrecompilesHandle<'a, ChainSpecT, DB> = + Arc ContextPrecompiles + 'a>; /// Load access list accounts and beneficiary. /// There is no need to load Caller as it is assumed that /// it will be loaded in DeductCallerHandle. -pub type LoadAccountsHandle<'a, EXT, DB> = - Arc) -> Result<(), EVMError<::Error>> + 'a>; +pub type LoadAccountsHandle<'a, ChainSpecT, EXT, DB> = Arc< + dyn Fn( + &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + + 'a, +>; /// Deduct the caller to its limit. -pub type DeductCallerHandle<'a, EXT, DB> = - Arc) -> EVMResultGeneric<(), ::Error> + 'a>; +pub type DeductCallerHandle<'a, ChainSpecT, EXT, DB> = Arc< + dyn Fn( + &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + + 'a, +>; /// Handles related to pre execution before the stack loop is started. -pub struct PreExecutionHandler<'a, EXT, DB: Database> { +pub struct PreExecutionHandler<'a, ChainSpecT: ChainSpec, EXT, DB: Database> { /// Load precompiles - pub load_precompiles: LoadPrecompilesHandle<'a, DB>, + pub load_precompiles: LoadPrecompilesHandle<'a, ChainSpecT, DB>, /// Main load handle - pub load_accounts: LoadAccountsHandle<'a, EXT, DB>, + pub load_accounts: LoadAccountsHandle<'a, ChainSpecT, EXT, DB>, /// Deduct max value from the caller. - pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, + pub deduct_caller: DeductCallerHandle<'a, ChainSpecT, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> PreExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT: 'a, DB: Database + 'a> + PreExecutionHandler<'a, ChainSpecT, EXT, DB> +{ /// Creates mainnet MainHandles. pub fn new() -> Self { Self { - load_precompiles: Arc::new(mainnet::load_precompiles::), - load_accounts: Arc::new(mainnet::load_accounts::), - deduct_caller: Arc::new(mainnet::deduct_caller::), + load_precompiles: Arc::new(mainnet::load_precompiles::), + load_accounts: Arc::new(mainnet::load_accounts::), + deduct_caller: Arc::new(mainnet::deduct_caller::), } } } -impl<'a, EXT, DB: Database> PreExecutionHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> PreExecutionHandler<'a, ChainSpecT, EXT, DB> { /// Deduct caller to its limit. - pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + pub fn deduct_caller( + &self, + context: &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.deduct_caller)(context) } /// Main load - pub fn load_accounts(&self, context: &mut Context) -> Result<(), EVMError> { + pub fn load_accounts( + &self, + context: &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.load_accounts)(context) } /// Load precompiles - pub fn load_precompiles(&self) -> ContextPrecompiles { + pub fn load_precompiles(&self) -> ContextPrecompiles { (self.load_precompiles)() } } diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs index 8aa55c28c2..df00f724b0 100644 --- a/crates/revm/src/handler/handle_types/validation.rs +++ b/crates/revm/src/handler/handle_types/validation.rs @@ -1,60 +1,77 @@ use crate::{ handler::mainnet, - primitives::{db::Database, EVMError, Env, Spec}, + primitives::{ + db::Database, ChainSpec, EVMResultGeneric, Env, InvalidTransaction, Spec, + TransactionValidation, + }, Context, }; use std::sync::Arc; /// Handle that validates env. -pub type ValidateEnvHandle<'a, DB> = - Arc Result<(), EVMError<::Error>> + 'a>; +pub type ValidateEnvHandle<'a, ChainSpecT, DB> = + Arc) -> EVMResultGeneric<(), ChainSpecT, ::Error> + 'a>; /// Handle that validates transaction environment against the state. /// Second parametar is initial gas. -pub type ValidateTxEnvAgainstState<'a, EXT, DB> = - Arc) -> Result<(), EVMError<::Error>> + 'a>; +pub type ValidateTxEnvAgainstState<'a, ChainSpecT, EXT, DB> = Arc< + dyn Fn( + &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, ::Error> + + 'a, +>; /// Initial gas calculation handle -pub type ValidateInitialTxGasHandle<'a, DB> = - Arc Result::Error>> + 'a>; +pub type ValidateInitialTxGasHandle<'a, ChainSpecT, DB> = Arc< + dyn Fn(&Env) -> EVMResultGeneric::Error> + 'a, +>; /// Handles related to validation. -pub struct ValidationHandler<'a, EXT, DB: Database> { +pub struct ValidationHandler<'a, ChainSpecT: ChainSpec, EXT, DB: Database> { /// Validate and calculate initial transaction gas. - pub initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, + pub initial_tx_gas: ValidateInitialTxGasHandle<'a, ChainSpecT, DB>, /// Validate transactions against state data. - pub tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, + pub tx_against_state: ValidateTxEnvAgainstState<'a, ChainSpecT, EXT, DB>, /// Validate Env. - pub env: ValidateEnvHandle<'a, DB>, + pub env: ValidateEnvHandle<'a, ChainSpecT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> ValidationHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT: 'a, DB: Database + 'a> + ValidationHandler<'a, ChainSpecT, EXT, DB> +where + ::ValidationError: From, +{ /// Create new ValidationHandles pub fn new() -> Self { Self { - initial_tx_gas: Arc::new(mainnet::validate_initial_tx_gas::), - env: Arc::new(mainnet::validate_env::), - tx_against_state: Arc::new(mainnet::validate_tx_against_state::), + initial_tx_gas: Arc::new(mainnet::validate_initial_tx_gas::), + env: Arc::new(mainnet::validate_env::), + tx_against_state: Arc::new( + mainnet::validate_tx_against_state::, + ), } } } -impl<'a, EXT, DB: Database> ValidationHandler<'a, EXT, DB> { +impl<'a, ChainSpecT: ChainSpec, EXT, DB: Database> ValidationHandler<'a, ChainSpecT, EXT, DB> { /// Validate env. - pub fn env(&self, env: &Env) -> Result<(), EVMError> { + pub fn env(&self, env: &Env) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.env)(env) } /// Initial gas - pub fn initial_tx_gas(&self, env: &Env) -> Result> { + pub fn initial_tx_gas( + &self, + env: &Env, + ) -> EVMResultGeneric { (self.initial_tx_gas)(env) } /// Validate ttansaction against the state. pub fn tx_against_state( &self, - context: &mut Context, - ) -> Result<(), EVMError> { + context: &mut Context, + ) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { (self.tx_against_state)(context) } } diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index 89094d771e..649c02fcd3 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -5,7 +5,7 @@ use crate::{ return_ok, return_revert, CallInputs, CreateInputs, CreateOutcome, Gas, InstructionResult, SharedMemory, }, - primitives::{EVMError, Env, Spec, SpecId}, + primitives::{ChainSpec, EVMError, EVMResultGeneric, Env, Spec, SpecId, Transaction}, CallFrame, Context, CreateFrame, Frame, FrameOrResult, FrameResult, }; use core::mem; @@ -17,12 +17,12 @@ use std::boxed::Box; /// Execute frame #[inline] -pub fn execute_frame( +pub fn execute_frame( frame: &mut Frame, shared_memory: &mut SharedMemory, - instruction_tables: &InstructionTables<'_, Context>, - context: &mut Context, -) -> Result> { + instruction_tables: &InstructionTables<'_, Context>, + context: &mut Context, +) -> EVMResultGeneric { let interpreter = frame.interpreter_mut(); let memory = mem::replace(shared_memory, EMPTY_SHARED_MEMORY); let next_action = match instruction_tables { @@ -37,8 +37,8 @@ pub fn execute_frame( /// Helper function called inside [`last_frame_return`] #[inline] -pub fn frame_return_with_refund_flag( - env: &Env, +pub fn frame_return_with_refund_flag( + env: &Env, frame_result: &mut FrameResult, refund_enabled: bool, ) { @@ -48,7 +48,7 @@ pub fn frame_return_with_refund_flag( let refunded = gas.refunded(); // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(env.tx.gas_limit); + *gas = Gas::new_spent(env.tx.gas_limit()); match instruction_result { return_ok!() => { @@ -73,29 +73,29 @@ pub fn frame_return_with_refund_flag( /// Handle output of the transaction #[inline] -pub fn last_frame_return( - context: &mut Context, +pub fn last_frame_return( + context: &mut Context, frame_result: &mut FrameResult, -) -> Result<(), EVMError> { - frame_return_with_refund_flag::(&context.evm.env, frame_result, true); +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + frame_return_with_refund_flag::(&context.evm.env, frame_result, true); Ok(()) } /// Handle frame sub call. #[inline] -pub fn call( - context: &mut Context, +pub fn call( + context: &mut Context, inputs: Box, -) -> Result> { +) -> EVMResultGeneric { context.evm.make_call_frame(&inputs) } #[inline] -pub fn call_return( - context: &mut Context, +pub fn call_return( + context: &mut Context, frame: Box, interpreter_result: InterpreterResult, -) -> Result> { +) -> EVMResultGeneric { context .evm .call_return(&interpreter_result, frame.frame_data.checkpoint); @@ -106,13 +106,14 @@ pub fn call_return( } #[inline] -pub fn insert_call_outcome( - context: &mut Context, +pub fn insert_call_outcome( + context: &mut Context, frame: &mut Frame, shared_memory: &mut SharedMemory, outcome: CallOutcome, -) -> Result<(), EVMError> { - context.evm.take_error()?; +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + context.evm.take_error().map_err(EVMError::Database)?; + frame .frame_data_mut() .interpreter @@ -122,19 +123,22 @@ pub fn insert_call_outcome( /// Handle frame sub create. #[inline] -pub fn create( - context: &mut Context, +pub fn create( + context: &mut Context, inputs: Box, -) -> Result> { - context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) +) -> EVMResultGeneric { + context + .evm + .make_create_frame(SPEC::SPEC_ID, &inputs) + .map_err(EVMError::Database) } #[inline] -pub fn create_return( - context: &mut Context, +pub fn create_return( + context: &mut Context, frame: Box, mut interpreter_result: InterpreterResult, -) -> Result> { +) -> EVMResultGeneric { context.evm.create_return::( &mut interpreter_result, frame.created_address, @@ -147,12 +151,13 @@ pub fn create_return( } #[inline] -pub fn insert_create_outcome( - context: &mut Context, +pub fn insert_create_outcome( + context: &mut Context, frame: &mut Frame, outcome: CreateOutcome, -) -> Result<(), EVMError> { - context.evm.take_error()?; +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + context.evm.take_error().map_err(EVMError::Database)?; + frame .frame_data_mut() .interpreter @@ -162,19 +167,22 @@ pub fn insert_create_outcome( /// Handle frame sub create. #[inline] -pub fn eofcreate( - context: &mut Context, +pub fn eofcreate( + context: &mut Context, inputs: Box, -) -> Result> { - context.evm.make_eofcreate_frame(SPEC::SPEC_ID, &inputs) +) -> EVMResultGeneric { + context + .evm + .make_eofcreate_frame(SPEC::SPEC_ID, &inputs) + .map_err(EVMError::Database) } #[inline] -pub fn eofcreate_return( - context: &mut Context, +pub fn eofcreate_return( + context: &mut Context, frame: Box, mut interpreter_result: InterpreterResult, -) -> Result> { +) -> EVMResultGeneric { context.evm.eofcreate_return::( &mut interpreter_result, frame.created_address, @@ -187,12 +195,13 @@ pub fn eofcreate_return( } #[inline] -pub fn insert_eofcreate_outcome( - context: &mut Context, +pub fn insert_eofcreate_outcome( + context: &mut Context, frame: &mut Frame, outcome: CreateOutcome, -) -> Result<(), EVMError> { - core::mem::replace(&mut context.evm.error, Ok(()))?; +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + core::mem::replace(&mut context.evm.error, Ok(())).map_err(EVMError::Database)?; + frame .frame_data_mut() .interpreter @@ -203,12 +212,13 @@ pub fn insert_eofcreate_outcome( #[cfg(test)] mod tests { use super::*; + use crate::primitives::EthChainSpec; use revm_interpreter::primitives::CancunSpec; use revm_precompile::Bytes; /// Creates frame result. fn call_last_frame_return(instruction_result: InstructionResult, gas: Gas) -> Gas { - let mut env = Env::default(); + let mut env = Env::::default(); env.tx.gas_limit = 100; let mut first_frame = FrameResult::Call(CallOutcome::new( @@ -219,7 +229,7 @@ mod tests { }, 0..0, )); - frame_return_with_refund_flag::(&env, &mut first_frame, true); + frame_return_with_refund_flag::(&env, &mut first_frame, true); *first_frame.gas() } diff --git a/crates/revm/src/handler/mainnet/post_execution.rs b/crates/revm/src/handler/mainnet/post_execution.rs index 0e5cb4e187..3866ce8a83 100644 --- a/crates/revm/src/handler/mainnet/post_execution.rs +++ b/crates/revm/src/handler/mainnet/post_execution.rs @@ -1,23 +1,24 @@ use crate::{ interpreter::{Gas, SuccessOrHalt}, primitives::{ - db::Database, EVMError, ExecutionResult, ResultAndState, Spec, SpecId::LONDON, U256, + db::Database, Block, ChainSpec, EVMError, EVMResultGeneric, ExecutionResult, + ResultAndState, Spec, SpecId::LONDON, Transaction, U256, }, Context, FrameResult, }; /// Mainnet end handle does not change the output. #[inline] -pub fn end( - _context: &mut Context, - evm_output: Result>, -) -> Result> { +pub fn end( + _context: &mut Context, + evm_output: EVMResultGeneric, ChainSpecT, DB::Error>, +) -> EVMResultGeneric, ChainSpecT, DB::Error> { evm_output } /// Clear handle clears error and journal state. #[inline] -pub fn clear(context: &mut Context) { +pub fn clear(context: &mut Context) { // clear error and journaled state. let _ = context.evm.take_error(); context.evm.inner.journaled_state.clear(); @@ -25,17 +26,17 @@ pub fn clear(context: &mut Context) { /// Reward beneficiary with gas fee. #[inline] -pub fn reward_beneficiary( - context: &mut Context, +pub fn reward_beneficiary( + context: &mut Context, gas: &Gas, -) -> Result<(), EVMError> { - let beneficiary = context.evm.env.block.coinbase; +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + let beneficiary = *context.evm.env.block.coinbase(); let effective_gas_price = context.evm.env.effective_gas_price(); // transfer fee to coinbase/beneficiary. // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(context.evm.env.block.basefee) + effective_gas_price.saturating_sub(*context.evm.env.block.basefee()) } else { effective_gas_price }; @@ -44,7 +45,8 @@ pub fn reward_beneficiary( .evm .inner .journaled_state - .load_account(beneficiary, &mut context.evm.inner.db)?; + .load_account(beneficiary, &mut context.evm.inner.db) + .map_err(EVMError::Database)?; coinbase_account.mark_touch(); coinbase_account.info.balance = coinbase_account @@ -56,11 +58,11 @@ pub fn reward_beneficiary( } #[inline] -pub fn reimburse_caller( - context: &mut Context, +pub fn reimburse_caller( + context: &mut Context, gas: &Gas, -) -> Result<(), EVMError> { - let caller = context.evm.env.tx.caller; +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { + let caller = context.evm.env.tx.caller(); let effective_gas_price = context.evm.env.effective_gas_price(); // return balance of not spend gas. @@ -68,7 +70,8 @@ pub fn reimburse_caller( .evm .inner .journaled_state - .load_account(caller, &mut context.evm.inner.db)?; + .load_account(*caller, &mut context.evm.inner.db) + .map_err(EVMError::Database)?; caller_account.info.balance = caller_account .info @@ -80,11 +83,12 @@ pub fn reimburse_caller( /// Main return handle, returns the output of the transaction. #[inline] -pub fn output( - context: &mut Context, +pub fn output( + context: &mut Context, result: FrameResult, -) -> Result> { - context.evm.take_error()?; +) -> EVMResultGeneric, ChainSpecT, DB::Error> { + context.evm.take_error().map_err(EVMError::Database)?; + // used gas with refund calculated. let gas_refunded = result.gas().refunded() as u64; let final_gas_used = result.gas().spent() - gas_refunded; @@ -94,7 +98,7 @@ pub fn output( // reset journal and return present state. let (state, logs) = context.evm.journaled_state.finalize(); - let result = match instruction_result.result.into() { + let result = match SuccessOrHalt::::from(instruction_result.result) { SuccessOrHalt::Success(reason) => ExecutionResult::Success { reason, gas_used: final_gas_used, diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs index adf0e3bd4f..26089a58ec 100644 --- a/crates/revm/src/handler/mainnet/pre_execution.rs +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -5,61 +5,69 @@ use crate::{ precompile::PrecompileSpecId, primitives::{ - db::Database, - Account, EVMError, Env, Spec, - SpecId::{CANCUN, PRAGUE, SHANGHAI}, - TxKind, BLOCKHASH_STORAGE_ADDRESS, U256, + db::Database, Account, Block, ChainSpec, EVMError, EVMResultGeneric, Env, Spec, SpecId, + Transaction as _, BLOCKHASH_STORAGE_ADDRESS, U256, }, Context, ContextPrecompiles, }; /// Main precompile load #[inline] -pub fn load_precompiles() -> ContextPrecompiles { +pub fn load_precompiles( +) -> ContextPrecompiles { ContextPrecompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)) } /// Main load handle #[inline] -pub fn load_accounts( - context: &mut Context, -) -> Result<(), EVMError> { +pub fn load_accounts( + context: &mut Context, +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { // set journaling state flag. context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); // load coinbase // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - context.evm.inner.journaled_state.initial_account_load( - context.evm.inner.env.block.coinbase, - [], - &mut context.evm.inner.db, - )?; + if SPEC::enabled(SpecId::SHANGHAI) { + context + .evm + .inner + .journaled_state + .initial_account_load( + *context.evm.inner.env.block.coinbase(), + [], + &mut context.evm.inner.db, + ) + .map_err(EVMError::Database)?; } // Load blockhash storage address // EIP-2935: Serve historical block hashes from state - if SPEC::enabled(PRAGUE) { - context.evm.inner.journaled_state.initial_account_load( - BLOCKHASH_STORAGE_ADDRESS, - [], - &mut context.evm.inner.db, - )?; + if SPEC::enabled(SpecId::PRAGUE) { + context + .evm + .inner + .journaled_state + .initial_account_load(BLOCKHASH_STORAGE_ADDRESS, [], &mut context.evm.inner.db) + .map_err(EVMError::Database)?; } - context.evm.load_access_list()?; + context.evm.load_access_list().map_err(EVMError::Database)?; Ok(()) } /// Helper function that deducts the caller balance. #[inline] -pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { +pub fn deduct_caller_inner( + caller_account: &mut Account, + env: &Env, +) { // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); + let mut gas_cost = U256::from(env.tx.gas_limit()).saturating_mul(env.effective_gas_price()); // EIP-4844 - if SPEC::enabled(CANCUN) { + if SPEC::enabled(SpecId::CANCUN) { let data_fee = env.calc_data_fee().expect("already checked"); gas_cost = gas_cost.saturating_add(data_fee); } @@ -68,7 +76,7 @@ pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if matches!(env.tx.transact_to, TxKind::Call(_)) { + if env.tx.kind().is_call() { // Nonce is already checked caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); } @@ -79,18 +87,22 @@ pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) /// Deducts the caller balance to the transaction limit. #[inline] -pub fn deduct_caller( - context: &mut Context, -) -> Result<(), EVMError> { +pub fn deduct_caller( + context: &mut Context, +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> { // load caller's account. let (caller_account, _) = context .evm .inner .journaled_state - .load_account(context.evm.inner.env.tx.caller, &mut context.evm.inner.db)?; + .load_account( + *context.evm.inner.env.tx.caller(), + &mut context.evm.inner.db, + ) + .map_err(EVMError::Database)?; // deduct gas cost from caller's account. - deduct_caller_inner::(caller_account, &context.evm.inner.env); + deduct_caller_inner::(caller_account, &context.evm.inner.env); Ok(()) } diff --git a/crates/revm/src/handler/mainnet/validation.rs b/crates/revm/src/handler/mainnet/validation.rs index 176e0e8282..045d179336 100644 --- a/crates/revm/src/handler/mainnet/validation.rs +++ b/crates/revm/src/handler/mainnet/validation.rs @@ -1,54 +1,72 @@ use revm_interpreter::gas; use crate::{ - primitives::{db::Database, EVMError, Env, InvalidTransaction, Spec}, + primitives::{ + db::Database, ChainSpec, EVMError, EVMResultGeneric, Env, InvalidTransaction, Spec, + Transaction, TransactionValidation, + }, Context, }; /// Validate environment for the mainnet. -pub fn validate_env(env: &Env) -> Result<(), EVMError> { +pub fn validate_env( + env: &Env, +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> +where + ::ValidationError: From, +{ // Important: validate block before tx. env.validate_block_env::()?; - env.validate_tx::()?; + env.validate_tx::() + .map_err(|error| EVMError::Transaction(error.into()))?; Ok(()) } /// Validates transaction against the state. -pub fn validate_tx_against_state( - context: &mut Context, -) -> Result<(), EVMError> { +pub fn validate_tx_against_state( + context: &mut Context, +) -> EVMResultGeneric<(), ChainSpecT, DB::Error> +where + ::ValidationError: From, +{ // load acc - let tx_caller = context.evm.env.tx.caller; + let tx_caller = context.evm.env.tx.caller(); let (caller_account, _) = context .evm .inner .journaled_state - .load_account(tx_caller, &mut context.evm.inner.db)?; + .load_account(*tx_caller, &mut context.evm.inner.db) + .map_err(EVMError::Database)?; context .evm .inner .env .validate_tx_against_state::(caller_account) - .map_err(EVMError::Transaction)?; + .map_err(|error| EVMError::Transaction(error.into()))?; Ok(()) } /// Validate initial transaction gas. -pub fn validate_initial_tx_gas( - env: &Env, -) -> Result> { - let input = &env.tx.data; - let is_create = env.tx.transact_to.is_create(); - let access_list = &env.tx.access_list; +pub fn validate_initial_tx_gas( + env: &Env, +) -> EVMResultGeneric +where + ::ValidationError: From, +{ + let input = &env.tx.data(); + let is_create = env.tx.kind().is_create(); + let access_list = env.tx.access_list(); let initial_gas_spend = gas::validate_initial_tx_gas(SPEC::SPEC_ID, input, is_create, access_list); // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); + if initial_gas_spend > env.tx.gas_limit() { + return Err(EVMError::Transaction( + InvalidTransaction::CallGasCostMoreThanGasLimit.into(), + )); } Ok(initial_gas_spend) } diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 4b9fa31377..074f645407 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -1,25 +1,27 @@ -use crate::{db::Database, handler::Handler, Context}; +use crate::{db::Database, handler::Handler, primitives::ChainSpec, Context}; use std::boxed::Box; /// EVM Handler -pub type EvmHandler<'a, EXT, DB> = Handler<'a, Context, EXT, DB>; +pub type EvmHandler<'a, ChainSpecT, EXT, DB> = + Handler<'a, ChainSpecT, Context, EXT, DB>; // Handle register -pub type HandleRegister = for<'a> fn(&mut EvmHandler<'a, EXT, DB>); +pub type HandleRegister = for<'a> fn(&mut EvmHandler<'a, ChainSpecT, EXT, DB>); // Boxed handle register -pub type HandleRegisterBox = Box Fn(&mut EvmHandler<'a, EXT, DB>)>; +pub type HandleRegisterBox = + Box Fn(&mut EvmHandler<'a, ChainSpecT, EXT, DB>)>; -pub enum HandleRegisters { +pub enum HandleRegisters { /// Plain function register - Plain(HandleRegister), + Plain(HandleRegister), /// Boxed function register. - Box(HandleRegisterBox), + Box(HandleRegisterBox), } -impl HandleRegisters { +impl HandleRegisters { /// Call register function to modify EvmHandler. - pub fn register(&self, handler: &mut EvmHandler<'_, EXT, DB>) { + pub fn register(&self, handler: &mut EvmHandler<'_, ChainSpecT, EXT, DB>) { match self { HandleRegisters::Plain(f) => f(handler), HandleRegisters::Box(f) => f(handler), diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 3225ed3546..92f2376fa2 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -12,7 +12,7 @@ use crate::{ interpreter::{ CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter, }, - primitives::{db::Database, Address, Log, U256}, + primitives::{db::Database, Address, ChainSpec, Log, U256}, EvmContext, }; use auto_impl::auto_impl; @@ -29,13 +29,17 @@ pub mod inspectors { /// EVM [Interpreter] callbacks. #[auto_impl(&mut, Box)] -pub trait Inspector { +pub trait Inspector { /// Called before the interpreter is initialized. /// /// If `interp.instruction_result` is set to anything other than [crate::interpreter::InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext, + ) { let _ = interp; let _ = context; } @@ -49,7 +53,7 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -59,14 +63,14 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [crate::interpreter::InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } /// Called when a log is emitted. #[inline] - fn log(&mut self, context: &mut EvmContext, log: &Log) { + fn log(&mut self, context: &mut EvmContext, log: &Log) { let _ = context; let _ = log; } @@ -77,7 +81,7 @@ pub trait Inspector { #[inline] fn call( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { let _ = context; @@ -93,7 +97,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -110,7 +114,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option { let _ = context; @@ -125,7 +129,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -139,7 +143,7 @@ pub trait Inspector { /// This can happen from create TX or from EOFCREATE opcode. fn eofcreate( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &mut EOFCreateInputs, ) -> Option { let _ = context; @@ -150,7 +154,7 @@ pub trait Inspector { /// Called when eof creating has ended. fn eofcreate_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &EOFCreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index ed42c4f7b4..faa104062b 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -8,7 +8,7 @@ use revm_interpreter::OpCode; use crate::{ inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::{Address, U256}, + primitives::{Address, ChainSpec, U256}, Database, EvmContext, Inspector, }; @@ -20,14 +20,18 @@ pub struct CustomPrintTracer { gas_inspector: GasInspector, } -impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { +impl Inspector for CustomPrintTracer { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext, + ) { self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let opcode = interp.current_opcode(); let name = OpCode::name_by_op(opcode); @@ -52,13 +56,13 @@ impl Inspector for CustomPrintTracer { self.gas_inspector.step(interp, context); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); } fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -67,7 +71,7 @@ impl Inspector for CustomPrintTracer { fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -76,7 +80,7 @@ impl Inspector for CustomPrintTracer { fn call( &mut self, - _context: &mut EvmContext, + _context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { println!( @@ -93,7 +97,7 @@ impl Inspector for CustomPrintTracer { fn create( &mut self, - _context: &mut EvmContext, + _context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option { println!( diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index aec20ffd8f..6d22653280 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -3,7 +3,7 @@ use crate::{ interpreter::{ CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult, }, - primitives::{db::Database, hex, HashMap, B256, U256}, + primitives::{db::Database, hex, ChainSpec, HashMap, Transaction, B256, U256}, EvmContext, Inspector, }; use revm_interpreter::OpCode; @@ -162,10 +162,10 @@ impl TracerEip3155 { self.output.flush() } - fn print_summary( + fn print_summary( &mut self, result: &InterpreterResult, - context: &mut EvmContext, + context: &mut EvmContext, ) { if self.print_summary { let spec_name: &str = context.spec_id().into(); @@ -173,7 +173,7 @@ impl TracerEip3155 { state_root: B256::ZERO.to_string(), output: result.output.to_string(), gas_used: hex_number( - context.inner.env().tx.gas_limit - self.gas_inspector.gas_remaining(), + context.inner.env().tx.gas_limit() - self.gas_inspector.gas_remaining(), ), pass: result.is_ok(), time: None, @@ -184,12 +184,16 @@ impl TracerEip3155 { } } -impl Inspector for TracerEip3155 { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { +impl Inspector for TracerEip3155 { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext, + ) { self.gas_inspector.initialize_interp(interp, context); } - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step(interp, context); self.stack.clone_from(interp.stack.data()); self.memory = if self.include_memory { @@ -204,7 +208,7 @@ impl Inspector for TracerEip3155 { self.refunded = interp.gas.refunded(); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; @@ -237,7 +241,7 @@ impl Inspector for TracerEip3155 { fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -254,7 +258,7 @@ impl Inspector for TracerEip3155 { fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 1100bd98e4..ea42b5781d 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -4,7 +4,7 @@ use revm_interpreter::CallOutcome; use crate::{ interpreter::{CallInputs, CreateInputs, CreateOutcome}, - primitives::db::Database, + primitives::{db::Database, ChainSpec}, EvmContext, Inspector, }; @@ -26,11 +26,11 @@ impl GasInspector { } } -impl Inspector for GasInspector { +impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext, + _context: &mut EvmContext, ) { self.gas_remaining = interp.gas.limit(); } @@ -38,7 +38,7 @@ impl Inspector for GasInspector { fn step( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext, + _context: &mut EvmContext, ) { self.gas_remaining = interp.gas.remaining(); } @@ -46,7 +46,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext, + _context: &mut EvmContext, ) { let remaining = interp.gas.remaining(); self.last_gas_cost = self.gas_remaining.saturating_sub(remaining); @@ -55,7 +55,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext, + _context: &mut EvmContext, _inputs: &CallInputs, mut outcome: CallOutcome, ) -> CallOutcome { @@ -68,7 +68,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext, + _context: &mut EvmContext, _inputs: &CreateInputs, mut outcome: CreateOutcome, ) -> CreateOutcome { @@ -82,16 +82,14 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { + use super::*; - use revm_interpreter::CallOutcome; - use revm_interpreter::CreateOutcome; + use crate::{interpreter::Interpreter, primitives::Log}; - use crate::{ - inspectors::GasInspector, - interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::Log, - Database, EvmContext, Inspector, - }; + #[cfg(feature = "optimism")] + type TestChainSpec = crate::optimism::OptimismChainSpec; + #[cfg(not(feature = "optimism"))] + type TestChainSpec = crate::primitives::EthChainSpec; #[derive(Default, Debug)] struct StackInspector { @@ -100,21 +98,25 @@ mod tests { gas_remaining_steps: Vec<(usize, u64)>, } - impl Inspector for StackInspector { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + impl Inspector for StackInspector { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext, + ) { self.gas_inspector.initialize_interp(interp, context); } - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.pc = interp.program_counter(); self.gas_inspector.step(interp, context); } - fn log(&mut self, context: &mut EvmContext, log: &Log) { + fn log(&mut self, context: &mut EvmContext, log: &Log) { self.gas_inspector.log(context, log); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); self.gas_remaining_steps .push((self.pc, self.gas_inspector.gas_remaining())); @@ -122,7 +124,7 @@ mod tests { fn call( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, call: &mut CallInputs, ) -> Option { self.gas_inspector.call(context, call) @@ -130,7 +132,7 @@ mod tests { fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -139,7 +141,7 @@ mod tests { fn create( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, call: &mut CreateInputs, ) -> Option { self.gas_inspector.create(context, call); @@ -148,7 +150,7 @@ mod tests { fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -183,14 +185,27 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + let mut evm = Evm::builder() + .with_chain_spec::() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .with_external_context(StackInspector::default()) .modify_tx_env(|tx| { - tx.clear(); - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.gas_limit = 21100; + *tx = ::Transaction::default(); + + #[cfg(feature = "optimism")] + let (caller, transact_to, gas_limit) = ( + &mut tx.base.caller, + &mut tx.base.transact_to, + &mut tx.base.gas_limit, + ); + + #[cfg(not(feature = "optimism"))] + let (caller, transact_to, gas_limit) = + (&mut tx.caller, &mut tx.transact_to, &mut tx.gas_limit); + + *caller = address!("1000000000000000000000000000000000000000"); + *transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); + *gas_limit = 21100; }) .append_handler_register(inspector_handle_register) .build(); diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 7c48284a03..1647f0c6ca 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -2,7 +2,7 @@ use crate::{ db::Database, handler::register::EvmHandler, interpreter::{opcode, InstructionResult, Interpreter}, - primitives::EVMError, + primitives::{ChainSpec, EVMResultGeneric}, Context, FrameOrResult, FrameResult, Inspector, JournalEntry, }; use core::cell::RefCell; @@ -10,14 +10,16 @@ use revm_interpreter::opcode::DynInstruction; use std::{rc::Rc, sync::Arc, vec::Vec}; /// Provides access to an `Inspector` instance. -pub trait GetInspector { +pub trait GetInspector { /// Returns the associated `Inspector`. - fn get_inspector(&mut self) -> &mut impl Inspector; + fn get_inspector(&mut self) -> &mut impl Inspector; } -impl> GetInspector for INSP { +impl> + GetInspector for INSP +{ #[inline] - fn get_inspector(&mut self) -> &mut impl Inspector { + fn get_inspector(&mut self) -> &mut impl Inspector { self } } @@ -34,8 +36,12 @@ impl> GetInspector for INSP { /// A few instructions handlers are wrapped twice once for `step` and `step_end` /// and in case of Logs and Selfdestruct wrapper is wrapped again for the /// `log` and `selfdestruct` calls. -pub fn inspector_handle_register>( - handler: &mut EvmHandler<'_, EXT, DB>, +pub fn inspector_handle_register< + ChainSpecT: ChainSpec, + DB: Database, + EXT: GetInspector, +>( + handler: &mut EvmHandler<'_, ChainSpecT, EXT, DB>, ) { let table = &mut handler.instruction_table; @@ -88,7 +94,7 @@ pub fn inspector_handle_register>( let create_input_stack_inner = create_input_stack.clone(); let prev_handle = handler.execution.create.clone(); handler.execution.create = Arc::new( - move |ctx, mut inputs| -> Result> { + move |ctx, mut inputs| -> EVMResultGeneric { let inspector = ctx.external.get_inspector(); // call inspector create to change input or return outcome. if let Some(outcome) = inspector.create(&mut ctx.evm, &mut inputs) { @@ -215,12 +221,13 @@ pub fn inspector_handle_register>( }); } -fn inspector_instruction( - prev: &DynInstruction<'_, Context>, +fn inspector_instruction( + prev: &DynInstruction<'_, Context>, interpreter: &mut Interpreter, - host: &mut Context, + host: &mut Context, ) where - INSP: GetInspector, + ChainSpecT: ChainSpec, + INSP: GetInspector, DB: Database, { // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the @@ -256,6 +263,11 @@ mod tests { Evm, EvmContext, }; + #[cfg(feature = "optimism")] + type TestChainSpec = crate::optimism::OptimismChainSpec; + #[cfg(not(feature = "optimism"))] + type TestChainSpec = crate::primitives::EthChainSpec; + #[derive(Default, Debug)] struct StackInspector { initialize_interp_called: bool, @@ -265,25 +277,33 @@ mod tests { call_end: bool, } - impl Inspector for StackInspector { - fn initialize_interp(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + impl Inspector for StackInspector { + fn initialize_interp( + &mut self, + _interp: &mut Interpreter, + _context: &mut EvmContext, + ) { if self.initialize_interp_called { unreachable!("initialize_interp should not be called twice") } self.initialize_interp_called = true; } - fn step(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + fn step(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { self.step += 1; } - fn step_end(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + fn step_end( + &mut self, + _interp: &mut Interpreter, + _context: &mut EvmContext, + ) { self.step_end += 1; } fn call( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, _call: &mut CallInputs, ) -> Option { if self.call { @@ -296,7 +316,7 @@ mod tests { fn call_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, _inputs: &CallInputs, outcome: CallOutcome, ) -> CallOutcome { @@ -310,7 +330,7 @@ mod tests { fn create( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, _call: &mut CreateInputs, ) -> Option { assert_eq!(context.journaled_state.depth(), 0); @@ -319,7 +339,7 @@ mod tests { fn create_end( &mut self, - context: &mut EvmContext, + context: &mut EvmContext, _inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { @@ -354,14 +374,27 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + let mut evm = Evm::builder() + .with_chain_spec::() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .with_external_context(StackInspector::default()) .modify_tx_env(|tx| { - tx.clear(); - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.gas_limit = 21100; + *tx = ::Transaction::default(); + + #[cfg(feature = "optimism")] + let (caller, transact_to, gas_limit) = ( + &mut tx.base.caller, + &mut tx.base.transact_to, + &mut tx.base.gas_limit, + ); + + #[cfg(not(feature = "optimism"))] + let (caller, transact_to, gas_limit) = + (&mut tx.caller, &mut tx.transact_to, &mut tx.gas_limit); + + *caller = address!("1000000000000000000000000000000000000000"); + *transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); + *gas_limit = 21100; }) .append_handler_register(inspector_handle_register) .build(); diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 9e9556e286..fe0c295683 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,6 +1,6 @@ -use crate::{Database, Inspector}; +use crate::{primitives::ChainSpec, Database, Inspector}; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; -impl Inspector for NoOpInspector {} +impl Inspector for NoOpInspector {} diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 3867ef43e8..f931cb8e26 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,9 +1,9 @@ use crate::{ interpreter::{InstructionResult, LoadAccountResult, SStoreResult, SelfDestructResult}, primitives::{ - db::Database, hash_map::Entry, Account, Address, Bytecode, EVMError, EvmState, - EvmStorageSlot, HashMap, HashSet, Log, SpecId, SpecId::*, TransientStorage, KECCAK_EMPTY, - PRECOMPILE3, U256, + db::Database, hash_map::Entry, Account, Address, Bytecode, EvmState, EvmStorageSlot, + HashMap, HashSet, Log, SpecId, SpecId::*, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, + U256, }, }; use core::mem; @@ -185,7 +185,7 @@ impl JournaledState { to: &Address, balance: U256, db: &mut DB, - ) -> Result, EVMError> { + ) -> Result, DB::Error> { // load accounts self.load_account(*from, db)?; self.load_account(*to, db)?; @@ -471,7 +471,7 @@ impl JournaledState { address: Address, target: Address, db: &mut DB, - ) -> Result> { + ) -> Result { let load_result = self.load_account_exist(target, db)?; if address != target { @@ -533,13 +533,12 @@ impl JournaledState { address: Address, storage_keys: impl IntoIterator, db: &mut DB, - ) -> Result<&mut Account, EVMError> { + ) -> Result<&mut Account, DB::Error> { // load or get account. let account = match self.state.entry(address) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(vac) => vac.insert( - db.basic(address) - .map_err(EVMError::Database)? + db.basic(address)? .map(|i| i.into()) .unwrap_or(Account::new_not_existing()), ), @@ -547,9 +546,7 @@ impl JournaledState { // preload storages. for storage_key in storage_keys.into_iter() { if let Entry::Vacant(entry) = account.storage.entry(storage_key) { - let storage = db - .storage(address, storage_key) - .map_err(EVMError::Database)?; + let storage = db.storage(address, storage_key)?; entry.insert(EvmStorageSlot::new(storage)); } } @@ -562,7 +559,7 @@ impl JournaledState { &mut self, address: Address, db: &mut DB, - ) -> Result<(&mut Account, bool), EVMError> { + ) -> Result<(&mut Account, bool), DB::Error> { let (value, is_cold) = match self.state.entry(address) { Entry::Occupied(entry) => { let account = entry.into_mut(); @@ -570,12 +567,11 @@ impl JournaledState { (account, is_cold) } Entry::Vacant(vac) => { - let account = - if let Some(account) = db.basic(address).map_err(EVMError::Database)? { - account.into() - } else { - Account::new_not_existing() - }; + let account = if let Some(account) = db.basic(address)? { + account.into() + } else { + Account::new_not_existing() + }; // precompiles are warm loaded so we need to take that into account let is_cold = !self.warm_preloaded_addresses.contains(&address); @@ -603,7 +599,7 @@ impl JournaledState { &mut self, address: Address, db: &mut DB, - ) -> Result> { + ) -> Result { let spec = self.spec; let (acc, is_cold) = self.load_account(address, db)?; @@ -625,16 +621,14 @@ impl JournaledState { &mut self, address: Address, db: &mut DB, - ) -> Result<(&mut Account, bool), EVMError> { + ) -> Result<(&mut Account, bool), DB::Error> { let (acc, is_cold) = self.load_account(address, db)?; if acc.info.code.is_none() { if acc.info.code_hash == KECCAK_EMPTY { let empty = Bytecode::default(); acc.info.code = Some(empty); } else { - let code = db - .code_by_hash(acc.info.code_hash) - .map_err(EVMError::Database)?; + let code = db.code_by_hash(acc.info.code_hash)?; acc.info.code = Some(code); } } @@ -652,7 +646,7 @@ impl JournaledState { address: Address, key: U256, db: &mut DB, - ) -> Result<(U256, bool), EVMError> { + ) -> Result<(U256, bool), DB::Error> { // assume acc is warm let account = self.state.get_mut(&address).unwrap(); // only if account is created in this tx we can assume that storage is empty. @@ -668,7 +662,7 @@ impl JournaledState { let value = if is_newly_created { U256::ZERO } else { - db.storage(address, key).map_err(EVMError::Database)? + db.storage(address, key)? }; vac.insert(EvmStorageSlot::new(value)); @@ -701,7 +695,7 @@ impl JournaledState { key: U256, new: U256, db: &mut DB, - ) -> Result> { + ) -> Result { // assume that acc exists and load the slot. let (present, is_cold) = self.sload(address, key, db)?; let acc = self.state.get_mut(&address).unwrap(); diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 5097388526..cb42740d71 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -29,7 +29,7 @@ pub use builder::EvmBuilder; pub use context::{ Context, ContextPrecompile, ContextPrecompiles, ContextStatefulPrecompile, ContextStatefulPrecompileArc, ContextStatefulPrecompileBox, ContextStatefulPrecompileMut, - ContextWithHandlerCfg, EvmContext, InnerEvmContext, + ContextWithChainSpec, EvmContext, InnerEvmContext, }; pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, @@ -37,7 +37,7 @@ pub use db::{ pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; pub use evm::{Evm, CALL_STACK_LIMIT}; pub use frame::{CallFrame, CreateFrame, Frame, FrameData, FrameOrResult, FrameResult}; -pub use handler::Handler; +pub use handler::{register::EvmHandler, Handler}; pub use inspector::{inspector_handle_register, inspectors, GetInspector, Inspector}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; // export Optimism types, helpers, and constants diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 12f3db9969..a54a0eb45a 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,11 +1,17 @@ //! Optimism-specific constants, types, and helpers. +mod env; mod fast_lz; +mod handler; mod handler_register; mod l1block; +mod result; +mod spec; pub use handler_register::{ - deduct_caller, end, last_frame_return, load_accounts, load_precompiles, - optimism_handle_register, output, reward_beneficiary, validate_env, validate_tx_against_state, + deduct_caller, end, last_frame_return, load_precompiles, optimism_handle_register, output, + reward_beneficiary, validate_env, validate_tx_against_state, }; pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; +pub use result::{InvalidOptimismTransaction, OptimismHaltReason}; +pub use spec::*; diff --git a/crates/revm/src/optimism/env.rs b/crates/revm/src/optimism/env.rs new file mode 100644 index 0000000000..a45bfca1e7 --- /dev/null +++ b/crates/revm/src/optimism/env.rs @@ -0,0 +1,169 @@ +use crate::{ + primitives::{ + db::Database, AccessListItem, Address, BlobExcessGasAndPrice, Block, BlockEnv, Bytes, + Transaction, TransactionValidation, TxEnv, TxKind, B256, U256, + }, + L1BlockInfo, +}; + +use super::{InvalidOptimismTransaction, OptimismSpecId}; + +/// The Optimism block environment. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct OptimismBlock { + pub base: BlockEnv, + /// L1 block info used to compute the L1-cost fee. + /// + /// Needs to be provided for Optimism non-deposit transactions. + l1_block_info: Option, +} + +impl OptimismBlock { + /// Constructs a new instance. + pub fn new(base: BlockEnv, l1_block_info: Option) -> Self { + Self { + base, + l1_block_info, + } + } + + /// Create a new Optimism block environment. + pub fn with_l1_block_info( + base: BlockEnv, + db: &mut DB, + spec_id: OptimismSpecId, + ) -> Result { + let l1_block_info = L1BlockInfo::try_fetch(db, spec_id)?; + + Ok(Self { + base, + l1_block_info: Some(l1_block_info), + }) + } + + /// Retrieves the L1 block info. + pub fn l1_block_info(&self) -> Option<&L1BlockInfo> { + self.l1_block_info.as_ref() + } +} + +impl Block for OptimismBlock { + fn number(&self) -> &U256 { + self.base.number() + } + + fn coinbase(&self) -> &Address { + self.base.coinbase() + } + + fn timestamp(&self) -> &U256 { + self.base.timestamp() + } + + fn gas_limit(&self) -> &U256 { + self.base.gas_limit() + } + + fn basefee(&self) -> &U256 { + self.base.basefee() + } + + fn difficulty(&self) -> &U256 { + self.base.difficulty() + } + + fn prevrandao(&self) -> Option<&B256> { + self.base.prevrandao() + } + + fn blob_excess_gas_and_price(&self) -> Option<&BlobExcessGasAndPrice> { + self.base.blob_excess_gas_and_price() + } +} + +/// The Optimism transaction environment. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct OptimismTransaction { + #[cfg_attr(feature = "serde", serde(flatten))] + pub base: TxEnv, + + /// The source hash is used to make sure that deposit transactions do + /// not have identical hashes. + /// + /// L1 originated deposit transaction source hashes are computed using + /// the hash of the l1 block hash and the l1 log index. + /// L1 attributes deposit source hashes are computed with the l1 block + /// hash and the sequence number = l2 block number - l2 epoch start + /// block number. + /// + /// These two deposit transaction sources specify a domain in the outer + /// hash so there are no collisions. + pub source_hash: Option, + /// The amount to increase the balance of the `from` account as part of + /// a deposit transaction. This is unconditional and is applied to the + /// `from` account even if the deposit transaction fails since + /// the deposit is pre-paid on L1. + pub mint: Option, + /// Whether or not the transaction is a system transaction. + pub is_system_transaction: Option, + /// An enveloped EIP-2718 typed transaction. This is used + /// to compute the L1 tx cost using the L1 block info, as + /// opposed to requiring downstream apps to compute the cost + /// externally. + pub enveloped_tx: Option, +} + +impl Transaction for OptimismTransaction { + fn caller(&self) -> &Address { + self.base.caller() + } + + fn gas_limit(&self) -> u64 { + self.base.gas_limit() + } + + fn gas_price(&self) -> &U256 { + self.base.gas_price() + } + + fn kind(&self) -> TxKind { + self.base.kind() + } + + fn value(&self) -> &U256 { + self.base.value() + } + + fn data(&self) -> &Bytes { + self.base.data() + } + + fn nonce(&self) -> u64 { + self.base.nonce() + } + + fn chain_id(&self) -> Option { + self.base.chain_id() + } + + fn access_list(&self) -> &[AccessListItem] { + self.base.access_list() + } + + fn max_priority_fee_per_gas(&self) -> Option<&U256> { + self.base.max_priority_fee_per_gas() + } + + fn blob_hashes(&self) -> &[B256] { + self.base.blob_hashes() + } + + fn max_fee_per_blob_gas(&self) -> Option<&U256> { + self.base.max_fee_per_blob_gas() + } +} + +impl TransactionValidation for OptimismTransaction { + type ValidationError = InvalidOptimismTransaction; +} diff --git a/crates/revm/src/optimism/handler.rs b/crates/revm/src/optimism/handler.rs new file mode 100644 index 0000000000..1002b429c6 --- /dev/null +++ b/crates/revm/src/optimism/handler.rs @@ -0,0 +1,19 @@ +use crate::{ + handler::register::{EvmHandler, HandleRegisters}, + primitives::db::Database, +}; + +use super::OptimismChainSpec; + +impl EvmHandler<'_, OptimismChainSpec, EXT, DB> { + /// Optimism with spec. Similar to [`Self::mainnet_with_spec`]. + pub fn optimism_with_spec(spec_id: crate::optimism::OptimismSpecId) -> Self { + let mut handler = Self::mainnet_with_spec(spec_id); + + handler.append_handler_register(HandleRegisters::Plain( + crate::optimism::optimism_handle_register::, + )); + + handler + } +} diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index 899ba41279..7f22f3dd7d 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -6,10 +6,10 @@ use crate::{ register::EvmHandler, }, interpreter::{return_ok, return_revert, Gas, InstructionResult}, - optimism, + optimism, optimism_spec_to_generic, primitives::{ - db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, HaltReason, - HashMap, InvalidTransaction, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, + db::Database, Account, Block, EVMError, Env, ExecutionResult, HashMap, InvalidTransaction, + ResultAndState, Transaction, U256, }, Context, ContextPrecompiles, FrameResult, }; @@ -18,8 +18,14 @@ use revm_precompile::{secp256r1, PrecompileSpecId}; use std::string::ToString; use std::sync::Arc; -pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EXT, DB>) { - spec_to_generic!(handler.cfg.spec_id, { +use super::{ + InvalidOptimismTransaction, OptimismChainSpec, OptimismHaltReason, OptimismSpec, OptimismSpecId, +}; + +pub fn optimism_handle_register( + handler: &mut EvmHandler<'_, OptimismChainSpec, EXT, DB>, +) { + optimism_spec_to_generic!(handler.spec_id, { // validate environment handler.validation.env = Arc::new(validate_env::); // Validate transaction against state. @@ -27,7 +33,8 @@ pub fn optimism_handle_register(handler: &mut EvmHandler<'_, // Load additional precompiles for the given chain spec. handler.pre_execution.load_precompiles = Arc::new(load_precompiles::); // load l1 data - handler.pre_execution.load_accounts = Arc::new(load_accounts::); + handler.pre_execution.load_accounts = + Arc::new(mainnet::load_accounts::); // An estimated batch cost is charged from the caller and added to L1 Fee Vault. handler.pre_execution.deduct_caller = Arc::new(deduct_caller::); // Refund is calculated differently then mainnet. @@ -40,45 +47,58 @@ pub fn optimism_handle_register(handler: &mut EvmHandler<'_, } /// Validate environment for the Optimism chain. -pub fn validate_env(env: &Env) -> Result<(), EVMError> { +pub fn validate_env( + env: &Env, +) -> Result<(), EVMError> { // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. - if env.tx.optimism.source_hash.is_some() { - return Ok(()); + if env.tx.source_hash.is_some() { + if env.block.l1_block_info().is_some() { + return Err(InvalidOptimismTransaction::UnexpectedL1BlockInfo.into()); + } else { + return Ok(()); + } } + // Important: validate block before tx. env.validate_block_env::()?; + if env.block.l1_block_info().is_none() { + return Err(InvalidOptimismTransaction::MissingL1BlockInfo.into()); + } + // Do not allow for a system transaction to be processed if Regolith is enabled. - let tx = &env.tx.optimism; - if tx.is_system_transaction.unwrap_or(false) && SPEC::enabled(SpecId::REGOLITH) { - return Err(InvalidTransaction::DepositSystemTxPostRegolith.into()); + if env.tx.is_system_transaction.unwrap_or(false) + && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) + { + return Err(InvalidOptimismTransaction::DepositSystemTxPostRegolith.into()); } - env.validate_tx::()?; + env.validate_tx::() + .map_err(InvalidOptimismTransaction::Base)?; Ok(()) } /// Don not perform any extra validation for deposit transactions, they are pre-verified on L1. -pub fn validate_tx_against_state( - context: &mut Context, -) -> Result<(), EVMError> { - if context.evm.inner.env.tx.optimism.source_hash.is_some() { +pub fn validate_tx_against_state( + context: &mut Context, +) -> Result<(), EVMError> { + if context.evm.inner.env.tx.source_hash.is_some() { return Ok(()); } - mainnet::validate_tx_against_state::(context) + mainnet::validate_tx_against_state::(context) } /// Handle output of the transaction #[inline] -pub fn last_frame_return( - context: &mut Context, +pub fn last_frame_return( + context: &mut Context, frame_result: &mut FrameResult, -) -> Result<(), EVMError> { +) -> Result<(), EVMError> { let env = context.evm.inner.env(); - let is_deposit = env.tx.optimism.source_hash.is_some(); - let tx_system = env.tx.optimism.is_system_transaction; - let tx_gas_limit = env.tx.gas_limit; - let is_regolith = SPEC::enabled(REGOLITH); + let is_deposit = env.tx.source_hash.is_some(); + let tx_system = env.tx.is_system_transaction; + let tx_gas_limit = env.tx.gas_limit(); + let is_regolith = SPEC::optimism_enabled(OptimismSpecId::REGOLITH); let instruction_result = frame_result.interpreter_result().result; let gas = frame_result.gas_mut(); @@ -135,17 +155,18 @@ pub fn last_frame_return( // Prior to Regolith, deposit transactions did not receive gas refunds. let is_gas_refund_disabled = env.cfg.is_gas_refund_disabled() || (is_deposit && !is_regolith); if !is_gas_refund_disabled { - gas.set_final_refund(SPEC::SPEC_ID.is_enabled_in(SpecId::LONDON)); + gas.set_final_refund(SPEC::OPTIMISM_SPEC_ID.is_enabled_in(OptimismSpecId::LONDON)); } Ok(()) } /// Load precompiles for Optimism chain. #[inline] -pub fn load_precompiles() -> ContextPrecompiles { +pub fn load_precompiles( +) -> ContextPrecompiles { let mut precompiles = ContextPrecompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)); - if SPEC::enabled(SpecId::FJORD) { + if SPEC::optimism_enabled(OptimismSpecId::FJORD) { precompiles.extend([ // EIP-7212: secp256r1 P256verify secp256r1::P256VERIFY, @@ -155,53 +176,38 @@ pub fn load_precompiles() -> ContextPrecompiles( - context: &mut Context, -) -> Result<(), EVMError> { - // the L1-cost fee is only computed for Optimism non-deposit transactions. - - if context.evm.inner.env.tx.optimism.source_hash.is_none() { - let l1_block_info = - crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.inner.db, SPEC::SPEC_ID) - .map_err(EVMError::Database)?; - - // storage l1 block info for later use. - context.evm.inner.l1_block_info = Some(l1_block_info); - } - - mainnet::load_accounts::(context) -} - /// Deduct max balance from caller #[inline] -pub fn deduct_caller( - context: &mut Context, -) -> Result<(), EVMError> { +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { // load caller's account. let (caller_account, _) = context .evm .inner .journaled_state - .load_account(context.evm.inner.env.tx.caller, &mut context.evm.inner.db)?; + .load_account( + *context.evm.inner.env.tx.caller(), + &mut context.evm.inner.db, + ) + .map_err(EVMError::Database)?; // If the transaction is a deposit with a `mint` value, add the mint value // in wei to the caller's balance. This should be persisted to the database // prior to the rest of execution. - if let Some(mint) = context.evm.inner.env.tx.optimism.mint { + if let Some(mint) = context.evm.inner.env.tx.mint { caller_account.info.balance += U256::from(mint); } // We deduct caller max balance after minting and before deducing the // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. - deduct_caller_inner::(caller_account, &context.evm.inner.env); + deduct_caller_inner::(caller_account, &context.evm.inner.env); // If the transaction is not a deposit transaction, subtract the L1 data fee from the // caller's balance directly after minting the requested amount of ETH. - if context.evm.inner.env.tx.optimism.source_hash.is_none() { + if context.evm.inner.env.tx.source_hash.is_none() { // get envelope - let Some(enveloped_tx) = &context.evm.inner.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.evm.inner.env.tx.enveloped_tx else { return Err(EVMError::Custom( "[OPTIMISM] Failed to load enveloped transaction.".to_string(), )); @@ -210,16 +216,18 @@ pub fn deduct_caller( let tx_l1_cost = context .evm .inner - .l1_block_info - .as_ref() + .env + .block + .l1_block_info() .expect("L1BlockInfo should be loaded") - .calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); + .calculate_tx_l1_cost(enveloped_tx, SPEC::OPTIMISM_SPEC_ID); if tx_l1_cost.gt(&caller_account.info.balance) { return Err(EVMError::Transaction( InvalidTransaction::LackOfFundForMaxFee { fee: tx_l1_cost.into(), balance: caller_account.info.balance.into(), - }, + } + .into(), )); } caller_account.info.balance = caller_account.info.balance.saturating_sub(tx_l1_cost); @@ -229,40 +237,43 @@ pub fn deduct_caller( /// Reward beneficiary with gas fee. #[inline] -pub fn reward_beneficiary( - context: &mut Context, +pub fn reward_beneficiary( + context: &mut Context, gas: &Gas, -) -> Result<(), EVMError> { - let is_deposit = context.evm.inner.env.tx.optimism.source_hash.is_some(); +) -> Result<(), EVMError> { + let is_deposit = context.evm.inner.env.tx.source_hash.is_some(); // transfer fee to coinbase/beneficiary. if !is_deposit { - mainnet::reward_beneficiary::(context, gas)?; + mainnet::reward_beneficiary::(context, gas)?; } if !is_deposit { // If the transaction is not a deposit transaction, fees are paid out // to both the Base Fee Vault as well as the L1 Fee Vault. - let Some(l1_block_info) = &context.evm.inner.l1_block_info else { - return Err(EVMError::Custom( - "[OPTIMISM] Failed to load L1 block information.".to_string(), - )); - }; + let l1_block_info = context + .evm + .inner + .env + .block + .l1_block_info() + .expect("L1BlockInfo should be loaded"); - let Some(enveloped_tx) = &context.evm.inner.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.evm.inner.env.tx.enveloped_tx else { return Err(EVMError::Custom( "[OPTIMISM] Failed to load enveloped transaction.".to_string(), )); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::OPTIMISM_SPEC_ID); // Send the L1 cost of the transaction to the L1 Fee Vault. let (l1_fee_vault_account, _) = context .evm .inner .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.inner.db)?; + .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.inner.db) + .map_err(EVMError::Database)?; l1_fee_vault_account.mark_touch(); l1_fee_vault_account.info.balance += l1_cost; @@ -271,14 +282,15 @@ pub fn reward_beneficiary( .evm .inner .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.inner.db)?; + .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.inner.db) + .map_err(EVMError::Database)?; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += context .evm .inner .env .block - .basefee + .basefee() .mul(U256::from(gas.spent() - gas.refunded() as u64)); } Ok(()) @@ -286,20 +298,20 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] -pub fn output( - context: &mut Context, +pub fn output( + context: &mut Context, frame_result: FrameResult, -) -> Result> { - let result = mainnet::output::(context, frame_result)?; +) -> Result, EVMError> { + let result = mainnet::output::(context, frame_result)?; if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. - let is_deposit = context.evm.inner.env.tx.optimism.source_hash.is_some(); - if is_deposit && SPEC::enabled(REGOLITH) { + let is_deposit = context.evm.inner.env.tx.source_hash.is_some(); + if is_deposit && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) { return Err(EVMError::Transaction( - InvalidTransaction::HaltedDepositPostRegolith, + InvalidOptimismTransaction::HaltedDepositPostRegolith, )); } } @@ -308,13 +320,16 @@ pub fn output( /// Optimism end handle changes output if the transaction is a deposit transaction. /// Deposit transaction can't be reverted and is always successful. #[inline] -pub fn end( - context: &mut Context, - evm_output: Result>, -) -> Result> { +pub fn end( + context: &mut Context, + evm_output: Result< + ResultAndState, + EVMError, + >, +) -> Result, EVMError> { evm_output.or_else(|err| { if matches!(err, EVMError::Transaction(_)) - && context.evm.inner.env().tx.optimism.source_hash.is_some() + && context.evm.inner.env().tx.source_hash.is_some() { // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -322,7 +337,7 @@ pub fn end( // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = context.evm.inner.env().tx.caller; + let caller = *context.evm.inner.env().tx.caller(); // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. @@ -336,9 +351,10 @@ pub fn end( .unwrap_or_default(), ); acc.info.nonce = acc.info.nonce.saturating_add(1); - acc.info.balance = acc.info.balance.saturating_add(U256::from( - context.evm.inner.env().tx.optimism.mint.unwrap_or(0), - )); + acc.info.balance = acc + .info + .balance + .saturating_add(U256::from(context.evm.inner.env().tx.mint.unwrap_or(0))); acc.mark_touch(); acc }; @@ -348,22 +364,16 @@ pub fn end( // limit of the transaction. pre-regolith, it is the gas limit // of the transaction for non system transactions and 0 for system // transactions. - let is_system_tx = context - .evm - .env() - .tx - .optimism - .is_system_transaction - .unwrap_or(false); - let gas_used = if SPEC::enabled(REGOLITH) || !is_system_tx { - context.evm.inner.env().tx.gas_limit + let is_system_tx = context.evm.env().tx.is_system_transaction.unwrap_or(false); + let gas_used = if SPEC::optimism_enabled(OptimismSpecId::REGOLITH) || !is_system_tx { + context.evm.inner.env().tx.gas_limit() } else { 0 }; Ok(ResultAndState { result: ExecutionResult::Halt { - reason: HaltReason::FailedDeposit, + reason: OptimismHaltReason::FailedDeposit, gas_used, }, state, @@ -381,16 +391,14 @@ mod tests { use super::*; use crate::{ db::{EmptyDB, InMemoryDB}, - primitives::{ - bytes, state::AccountInfo, Address, BedrockSpec, Bytes, Env, LatestSpec, RegolithSpec, - B256, - }, + optimism::{env::OptimismBlock, BedrockSpec, LatestSpec, RegolithSpec}, + primitives::{bytes, state::AccountInfo, Address, BlockEnv, Bytes, Env, B256}, L1BlockInfo, }; /// Creates frame result. - fn call_last_frame_return( - env: Env, + fn call_last_frame_return( + env: Env, instruction_result: InstructionResult, gas: Gas, ) -> Gas { @@ -410,9 +418,9 @@ mod tests { #[test] fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.tx.optimism.source_hash = None; + let mut env = Env::::default(); + env.tx.base.gas_limit = 100; + env.tx.source_hash = None; let gas = call_last_frame_return::(env, InstructionResult::Revert, Gas::new(90)); @@ -423,9 +431,9 @@ mod tests { #[test] fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.tx.optimism.source_hash = Some(B256::ZERO); + let mut env = Env::::default(); + env.tx.base.gas_limit = 100; + env.tx.source_hash = Some(B256::ZERO); let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); @@ -436,9 +444,9 @@ mod tests { #[test] fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.tx.optimism.source_hash = Some(B256::ZERO); + let mut env = Env::::default(); + env.tx.base.gas_limit = 100; + env.tx.source_hash = Some(B256::ZERO); let mut ret_gas = Gas::new(90); ret_gas.record_refund(20); @@ -457,9 +465,9 @@ mod tests { #[test] fn test_consume_gas_sys_deposit_tx() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.tx.optimism.source_hash = Some(B256::ZERO); + let mut env = Env::::default(); + env.tx.base.gas_limit = 100; + env.tx.source_hash = Some(B256::ZERO); let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 0); @@ -478,17 +486,21 @@ mod tests { ..Default::default() }, ); - let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); - context.evm.inner.l1_block_info = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); + + let mut context: Context = Context::new_with_db(db); + context.evm.inner.env.block = OptimismBlock::new( + BlockEnv::default(), + Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..L1BlockInfo::default() + }), + ); // Enveloped needs to be some but it will deduce zero fee. - context.evm.inner.env.tx.optimism.enveloped_tx = Some(bytes!("")); + context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); // added mint value is 10. - context.evm.inner.env.tx.optimism.mint = Some(10); + context.evm.inner.env.tx.mint = Some(10); deduct_caller::(&mut context).unwrap(); @@ -513,20 +525,23 @@ mod tests { ..Default::default() }, ); - let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); - context.evm.inner.l1_block_info = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); + let mut context: Context = Context::new_with_db(db); + context.evm.inner.env.block = OptimismBlock::new( + BlockEnv::default(), + Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..L1BlockInfo::default() + }), + ); // l1block cost is 1048 fee. - context.evm.inner.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); // added mint value is 10. - context.evm.inner.env.tx.optimism.mint = Some(10); + context.evm.inner.env.tx.mint = Some(10); // Putting source_hash to some makes it a deposit transaction. // so enveloped_tx gas cost is ignored. - context.evm.inner.env.tx.optimism.source_hash = Some(B256::ZERO); + context.evm.inner.env.tx.source_hash = Some(B256::ZERO); deduct_caller::(&mut context).unwrap(); @@ -551,15 +566,18 @@ mod tests { ..Default::default() }, ); - let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); - context.evm.inner.l1_block_info = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); + let mut context: Context = Context::new_with_db(db); + context.evm.inner.env.block = OptimismBlock::new( + BlockEnv::default(), + Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..L1BlockInfo::default() + }), + ); // l1block cost is 1048 fee. - context.evm.inner.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); deduct_caller::(&mut context).unwrap(); // Check the account balance is updated. @@ -583,15 +601,18 @@ mod tests { ..Default::default() }, ); - let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); - context.evm.inner.l1_block_info = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); + let mut context: Context = Context::new_with_db(db); + context.evm.inner.env.block = OptimismBlock::new( + BlockEnv::default(), + Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..L1BlockInfo::default() + }), + ); // l1block cost is 1048 fee. - context.evm.inner.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); assert_eq!( deduct_caller::(&mut context), @@ -599,7 +620,8 @@ mod tests { InvalidTransaction::LackOfFundForMaxFee { fee: Box::new(U256::from(1048)), balance: Box::new(U256::from(48)), - }, + } + .into(), )) ); } @@ -607,12 +629,23 @@ mod tests { #[test] fn test_validate_sys_tx() { // mark the tx as a system transaction. - let mut env = Env::default(); - env.tx.optimism.is_system_transaction = Some(true); + let mut env = Env:: { + block: OptimismBlock::new( + BlockEnv::default(), + Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: Some(U256::from(1_000)), + l1_base_fee_scalar: U256::from(1_000), + ..L1BlockInfo::default() + }), + ), + ..Env::default() + }; + env.tx.is_system_transaction = Some(true); assert_eq!( validate_env::(&env), Err(EVMError::Transaction( - InvalidTransaction::DepositSystemTxPostRegolith + InvalidOptimismTransaction::DepositSystemTxPostRegolith )) ); @@ -623,16 +656,16 @@ mod tests { #[test] fn test_validate_deposit_tx() { // Set source hash. - let mut env = Env::default(); - env.tx.optimism.source_hash = Some(B256::ZERO); + let mut env = Env::::default(); + env.tx.source_hash = Some(B256::ZERO); assert!(validate_env::(&env).is_ok()); } #[test] fn test_validate_tx_against_state_deposit_tx() { // Set source hash. - let mut env = Env::default(); - env.tx.optimism.source_hash = Some(B256::ZERO); + let mut env = Env::::default(); + env.tx.source_hash = Some(B256::ZERO); // Nonce and balance checks should be skipped for deposit transactions. assert!(validate_env::(&env).is_ok()); diff --git a/crates/revm/src/optimism/l1block.rs b/crates/revm/src/optimism/l1block.rs index e348718db2..64735e7daf 100644 --- a/crates/revm/src/optimism/l1block.rs +++ b/crates/revm/src/optimism/l1block.rs @@ -1,7 +1,9 @@ use crate::optimism::fast_lz::flz_compress_len; -use crate::primitives::{address, db::Database, Address, SpecId, U256}; +use crate::primitives::{address, db::Database, Address, U256}; use core::ops::Mul; +use super::OptimismSpecId; + const ZERO_BYTE_COST: u64 = 4; const NON_ZERO_BYTE_COST: u64 = 16; @@ -46,7 +48,7 @@ pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000 /// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) /// /// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct L1BlockInfo { /// The base fee of the L1 origin block. pub l1_base_fee: U256, @@ -64,16 +66,19 @@ pub struct L1BlockInfo { impl L1BlockInfo { /// Try to fetch the L1 block info from the database. - pub fn try_fetch(db: &mut DB, spec_id: SpecId) -> Result { + pub fn try_fetch( + db: &mut DB, + spec_id: OptimismSpecId, + ) -> Result { // Ensure the L1 Block account is loaded into the cache after Ecotone. With EIP-4788, it is no longer the case // that the L1 block account is loaded into the cache prior to the first inquiry for the L1 block info. - if spec_id.is_enabled_in(SpecId::CANCUN) { + if spec_id.is_enabled_in(OptimismSpecId::CANCUN) { let _ = db.basic(L1_BLOCK_CONTRACT)?; } let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - if !spec_id.is_enabled_in(SpecId::ECOTONE) { + if !spec_id.is_enabled_in(OptimismSpecId::ECOTONE) { let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; @@ -124,8 +129,8 @@ impl L1BlockInfo { /// /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to /// account for the empty signature. - pub fn data_gas(&self, input: &[u8], spec_id: SpecId) -> U256 { - if spec_id.is_enabled_in(SpecId::FJORD) { + pub fn data_gas(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { + if spec_id.is_enabled_in(OptimismSpecId::FJORD) { let estimated_size = self.tx_estimated_size_fjord(input); return estimated_size @@ -142,7 +147,7 @@ impl L1BlockInfo { })); // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !spec_id.is_enabled_in(SpecId::REGOLITH) { + if !spec_id.is_enabled_in(OptimismSpecId::REGOLITH) { rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); } @@ -161,16 +166,16 @@ impl L1BlockInfo { .max(U256::from(100_000_000)) } - /// Calculate the gas cost of a transaction based on L1 block data posted on L2, depending on the [SpecId] passed. - pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: SpecId) -> U256 { + /// Calculate the gas cost of a transaction based on L1 block data posted on L2, depending on the [OptimismSpecId] passed. + pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { // If the input is a deposit transaction or empty, the default value is zero. if input.is_empty() || input.first() == Some(&0x7F) { return U256::ZERO; } - if spec_id.is_enabled_in(SpecId::FJORD) { + if spec_id.is_enabled_in(OptimismSpecId::FJORD) { self.calculate_tx_l1_cost_fjord(input) - } else if spec_id.is_enabled_in(SpecId::ECOTONE) { + } else if spec_id.is_enabled_in(OptimismSpecId::ECOTONE) { self.calculate_tx_l1_cost_ecotone(input, spec_id) } else { self.calculate_tx_l1_cost_bedrock(input, spec_id) @@ -178,7 +183,7 @@ impl L1BlockInfo { } /// Calculate the gas cost of a transaction based on L1 block data posted on L2, pre-Ecotone. - fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: SpecId) -> U256 { + fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { let rollup_data_gas_cost = self.data_gas(input, spec_id); rollup_data_gas_cost .saturating_add(self.l1_fee_overhead.unwrap_or_default()) @@ -189,7 +194,7 @@ impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2, post-Ecotone. /// - /// [SpecId::ECOTONE] L1 cost function: + /// [OptimismSpecId::ECOTONE] L1 cost function: /// `(calldataGas/16)*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/1e6` /// /// We divide "calldataGas" by 16 to change from units of calldata gas to "estimated # of bytes when compressed". @@ -197,7 +202,7 @@ impl L1BlockInfo { /// /// Function is actually computed as follows for better precision under integer arithmetic: /// `calldataGas*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/16e6` - fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: SpecId) -> U256 { + fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { // There is an edgecase where, for the very first Ecotone block (unless it is activated at Genesis), we must // use the Bedrock cost function. To determine if this is the case, we can check if the Ecotone parameters are // unset. @@ -215,7 +220,7 @@ impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2, post-Fjord. /// - /// [SpecId::FJORD] L1 cost function: + /// [OptimismSpecId::FJORD] L1 cost function: /// `estimatedSize*(baseFeeScalar*l1BaseFee*16 + blobFeeScalar*l1BlobBaseFee)/1e12` fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 { let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone(); @@ -262,17 +267,17 @@ mod tests { // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 68 * 16 = 1136 let input = bytes!("FACADE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + let bedrock_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::BEDROCK); assert_eq!(bedrock_data_gas, U256::from(1136)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + let regolith_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::REGOLITH); assert_eq!(regolith_data_gas, U256::from(48)); // Fjord has a minimum compressed size of 100 bytes // gas cost = 100 * 16 = 1600 - let fjord_data_gas = l1_block_info.data_gas(&input, SpecId::FJORD); + let fjord_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::FJORD); assert_eq!(fjord_data_gas, U256::from(1600)); } @@ -292,17 +297,17 @@ mod tests { // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 let input = bytes!("FA00CA00DE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + let bedrock_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::BEDROCK); assert_eq!(bedrock_data_gas, U256::from(1144)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + let regolith_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::REGOLITH); assert_eq!(regolith_data_gas, U256::from(56)); // Fjord has a minimum compressed size of 100 bytes // gas cost = 100 * 16 = 1600 - let fjord_data_gas = l1_block_info.data_gas(&input, SpecId::FJORD); + let fjord_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::FJORD); assert_eq!(fjord_data_gas, U256::from(1600)); } @@ -316,17 +321,17 @@ mod tests { }; let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); assert_eq!(gas_cost, U256::from(1048)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); assert_eq!(gas_cost, U256::ZERO); } @@ -345,23 +350,23 @@ mod tests { // = (16 * 3) * (1000 * 16 * 1000 + 1000 * 1000) / (16 * 1e6) // = 51 let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); assert_eq!(gas_cost, U256::from(51)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); assert_eq!(gas_cost, U256::ZERO); // If the scalars are empty, the bedrock cost function should be used. l1_block_info.empty_scalars = true; let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); assert_eq!(gas_cost, U256::from(1048)); } @@ -386,7 +391,7 @@ mod tests { // l1Cost = estimatedSize * l1FeeScaled / 1e12 // = 100e6 * 17 / 1e6 // = 1700 - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); assert_eq!(gas_cost, U256::from(1700)); // fastLzSize = 202 @@ -397,17 +402,17 @@ mod tests { // l1Cost = estimatedSize * l1FeeScaled / 1e12 // = 126387400 * 17 / 1e6 // = 2148 - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); assert_eq!(gas_cost, U256::from(2148)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); assert_eq!(gas_cost, U256::ZERO); } } diff --git a/crates/revm/src/optimism/result.rs b/crates/revm/src/optimism/result.rs new file mode 100644 index 0000000000..e1ace4647a --- /dev/null +++ b/crates/revm/src/optimism/result.rs @@ -0,0 +1,98 @@ +use core::fmt::Display; + +use crate::primitives::{EVMError, HaltReason, InvalidTransaction}; + +/// Optimism transaction validation error. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum InvalidOptimismTransaction { + Base(InvalidTransaction), + /// System transactions are not supported post-regolith hardfork. + /// + /// Before the Regolith hardfork, there was a special field in the `Deposit` transaction + /// type that differentiated between `system` and `user` deposit transactions. This field + /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction + /// is found with this field set to `true` after the hardfork activation. + /// + /// In addition, this error is internal, and bubbles up into a [OptimismHaltReason::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this + /// case for failed deposit transactions. + DepositSystemTxPostRegolith, + /// Deposit transaction haults bubble up to the global main return handler, wiping state and + /// only increasing the nonce + persisting the mint value. + /// + /// This is a catch-all error for any deposit transaction that is results in a [HaltReason] error + /// post-regolith hardfork. This allows for a consumer to easily handle special cases where + /// a deposit transaction fails during validation, but must still be included in the block. + /// + /// In addition, this error is internal, and bubbles up into a [OptimismHaltReason::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this + /// case for failed deposit transactions. + HaltedDepositPostRegolith, + /// L1 block info is missing for a non-deposit transaction. + MissingL1BlockInfo, + /// L1 block info is provided for a deposit transaction. + UnexpectedL1BlockInfo, +} + +impl Display for InvalidOptimismTransaction { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Base(error) => error.fmt(f), + Self::DepositSystemTxPostRegolith => { + write!( + f, + "deposit system transactions post regolith hardfork are not supported" + ) + } + Self::HaltedDepositPostRegolith => { + write!( + f, + "deposit transaction halted post-regolith; error will be bubbled up to main return handler" + ) + } + Self::MissingL1BlockInfo => { + write!(f, "non-deposit transaction is missing L1 block info") + } + Self::UnexpectedL1BlockInfo => { + write!(f, "deposit transaction has unexpected L1 block info") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidOptimismTransaction {} + +impl From for InvalidOptimismTransaction { + fn from(value: InvalidTransaction) -> Self { + Self::Base(value) + } +} + +impl From for EVMError { + fn from(value: InvalidOptimismTransaction) -> Self { + Self::Transaction(value) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum OptimismHaltReason { + Base(HaltReason), + FailedDeposit, +} + +impl From for OptimismHaltReason { + fn from(value: HaltReason) -> Self { + Self::Base(value) + } +} diff --git a/crates/revm/src/optimism/spec.rs b/crates/revm/src/optimism/spec.rs new file mode 100644 index 0000000000..0bb0c9d267 --- /dev/null +++ b/crates/revm/src/optimism/spec.rs @@ -0,0 +1,791 @@ +use revm_precompile::PrecompileSpecId; + +use crate::primitives::{ChainSpec, Spec, SpecId}; + +use super::{ + env::{OptimismBlock, OptimismTransaction}, + OptimismHaltReason, +}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct OptimismChainSpec; + +impl ChainSpec for OptimismChainSpec { + type Block = OptimismBlock; + type Hardfork = OptimismSpecId; + type HaltReason = OptimismHaltReason; + type Transaction = OptimismTransaction; +} + +/// Specification IDs for the optimism blockchain. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[allow(non_camel_case_types)] +pub enum OptimismSpecId { + FRONTIER = 0, + FRONTIER_THAWING = 1, + HOMESTEAD = 2, + DAO_FORK = 3, + TANGERINE = 4, + SPURIOUS_DRAGON = 5, + BYZANTIUM = 6, + CONSTANTINOPLE = 7, + PETERSBURG = 8, + ISTANBUL = 9, + MUIR_GLACIER = 10, + BERLIN = 11, + LONDON = 12, + ARROW_GLACIER = 13, + GRAY_GLACIER = 14, + MERGE = 15, + BEDROCK = 16, + REGOLITH = 17, + SHANGHAI = 18, + CANYON = 19, + CANCUN = 20, + ECOTONE = 21, + FJORD = 22, + PRAGUE = 23, + PRAGUE_EOF = 24, + #[default] + LATEST = u8::MAX, +} + +impl OptimismSpecId { + /// Returns the `OptimismSpecId` for the given `u8`. + #[inline] + pub fn try_from_u8(spec_id: u8) -> Option { + Self::n(spec_id) + } + + /// Returns `true` if the given specification ID is enabled in this spec. + #[inline] + pub const fn is_enabled_in(self, other: Self) -> bool { + Self::enabled(self, other) + } + + /// Returns `true` if the given specification ID is enabled in this spec. + #[inline] + pub const fn enabled(our: Self, other: Self) -> bool { + our as u8 >= other as u8 + } + + /// Converts the `OptimismSpecId` into a `SpecId`. + const fn into_eth_spec_id(self) -> SpecId { + match self { + OptimismSpecId::FRONTIER => SpecId::FRONTIER, + OptimismSpecId::FRONTIER_THAWING => SpecId::FRONTIER_THAWING, + OptimismSpecId::HOMESTEAD => SpecId::HOMESTEAD, + OptimismSpecId::DAO_FORK => SpecId::DAO_FORK, + OptimismSpecId::TANGERINE => SpecId::TANGERINE, + OptimismSpecId::SPURIOUS_DRAGON => SpecId::SPURIOUS_DRAGON, + OptimismSpecId::BYZANTIUM => SpecId::BYZANTIUM, + OptimismSpecId::CONSTANTINOPLE => SpecId::CONSTANTINOPLE, + OptimismSpecId::PETERSBURG => SpecId::PETERSBURG, + OptimismSpecId::ISTANBUL => SpecId::ISTANBUL, + OptimismSpecId::MUIR_GLACIER => SpecId::MUIR_GLACIER, + OptimismSpecId::BERLIN => SpecId::BERLIN, + OptimismSpecId::LONDON => SpecId::LONDON, + OptimismSpecId::ARROW_GLACIER => SpecId::ARROW_GLACIER, + OptimismSpecId::GRAY_GLACIER => SpecId::GRAY_GLACIER, + OptimismSpecId::MERGE | OptimismSpecId::BEDROCK | OptimismSpecId::REGOLITH => { + SpecId::MERGE + } + OptimismSpecId::SHANGHAI | OptimismSpecId::CANYON => SpecId::SHANGHAI, + OptimismSpecId::CANCUN | OptimismSpecId::ECOTONE | OptimismSpecId::FJORD => { + SpecId::CANCUN + } + OptimismSpecId::PRAGUE => SpecId::PRAGUE, + OptimismSpecId::PRAGUE_EOF => SpecId::PRAGUE_EOF, + OptimismSpecId::LATEST => SpecId::LATEST, + } + } +} + +impl From for SpecId { + fn from(value: OptimismSpecId) -> Self { + value.into_eth_spec_id() + } +} + +impl From for OptimismSpecId { + fn from(value: SpecId) -> Self { + match value { + SpecId::FRONTIER => Self::FRONTIER, + SpecId::FRONTIER_THAWING => Self::FRONTIER_THAWING, + SpecId::HOMESTEAD => Self::HOMESTEAD, + SpecId::DAO_FORK => Self::DAO_FORK, + SpecId::TANGERINE => Self::TANGERINE, + SpecId::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, + SpecId::BYZANTIUM => Self::BYZANTIUM, + SpecId::CONSTANTINOPLE => Self::CONSTANTINOPLE, + SpecId::PETERSBURG => Self::PETERSBURG, + SpecId::ISTANBUL => Self::ISTANBUL, + SpecId::MUIR_GLACIER => Self::MUIR_GLACIER, + SpecId::BERLIN => Self::BERLIN, + SpecId::LONDON => Self::LONDON, + SpecId::ARROW_GLACIER => Self::ARROW_GLACIER, + SpecId::GRAY_GLACIER => Self::GRAY_GLACIER, + SpecId::MERGE => Self::MERGE, + SpecId::SHANGHAI => Self::SHANGHAI, + SpecId::CANCUN => Self::CANCUN, + SpecId::PRAGUE => Self::PRAGUE, + SpecId::PRAGUE_EOF => Self::PRAGUE_EOF, + SpecId::LATEST => Self::LATEST, + } + } +} + +impl From for PrecompileSpecId { + fn from(value: OptimismSpecId) -> Self { + PrecompileSpecId::from_spec_id(value.into_eth_spec_id()) + } +} + +impl From<&str> for OptimismSpecId { + fn from(name: &str) -> Self { + match name { + "Frontier" => Self::FRONTIER, + "Homestead" => Self::HOMESTEAD, + "Tangerine" => Self::TANGERINE, + "Spurious" => Self::SPURIOUS_DRAGON, + "Byzantium" => Self::BYZANTIUM, + "Constantinople" => Self::CONSTANTINOPLE, + "Petersburg" => Self::PETERSBURG, + "Istanbul" => Self::ISTANBUL, + "MuirGlacier" => Self::MUIR_GLACIER, + "Berlin" => Self::BERLIN, + "London" => Self::LONDON, + "Merge" => Self::MERGE, + "Shanghai" => Self::SHANGHAI, + "Cancun" => Self::CANCUN, + "Prague" => Self::PRAGUE, + "PragueEOF" => Self::PRAGUE_EOF, + "Bedrock" => Self::BEDROCK, + "Regolith" => Self::REGOLITH, + "Canyon" => Self::CANYON, + "Ecotone" => Self::ECOTONE, + "Fjord" => Self::FJORD, + _ => Self::LATEST, + } + } +} + +impl From for &'static str { + fn from(value: OptimismSpecId) -> Self { + match value { + OptimismSpecId::FRONTIER + | OptimismSpecId::FRONTIER_THAWING + | OptimismSpecId::HOMESTEAD + | OptimismSpecId::DAO_FORK + | OptimismSpecId::TANGERINE + | OptimismSpecId::SPURIOUS_DRAGON + | OptimismSpecId::BYZANTIUM + | OptimismSpecId::CONSTANTINOPLE + | OptimismSpecId::PETERSBURG + | OptimismSpecId::ISTANBUL + | OptimismSpecId::MUIR_GLACIER + | OptimismSpecId::BERLIN + | OptimismSpecId::LONDON + | OptimismSpecId::ARROW_GLACIER + | OptimismSpecId::GRAY_GLACIER + | OptimismSpecId::MERGE + | OptimismSpecId::SHANGHAI + | OptimismSpecId::CANCUN + | OptimismSpecId::PRAGUE + | OptimismSpecId::PRAGUE_EOF => value.into_eth_spec_id().into(), + OptimismSpecId::BEDROCK => "Bedrock", + OptimismSpecId::REGOLITH => "Regolith", + OptimismSpecId::CANYON => "Canyon", + OptimismSpecId::ECOTONE => "Ecotone", + OptimismSpecId::FJORD => "Fjord", + OptimismSpecId::LATEST => "Latest", + } + } +} + +pub trait OptimismSpec: Spec + Sized + 'static { + /// The specification ID for optimism. + const OPTIMISM_SPEC_ID: OptimismSpecId; + + /// Returns whether the provided `OptimismSpec` is enabled by this spec. + #[inline] + fn optimism_enabled(spec_id: OptimismSpecId) -> bool { + OptimismSpecId::enabled(Self::OPTIMISM_SPEC_ID, spec_id) + } +} + +macro_rules! spec { + ($spec_id:ident, $spec_name:ident) => { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $spec_name; + + impl OptimismSpec for $spec_name { + const OPTIMISM_SPEC_ID: OptimismSpecId = OptimismSpecId::$spec_id; + } + + impl Spec for $spec_name { + const SPEC_ID: SpecId = $spec_name::OPTIMISM_SPEC_ID.into_eth_spec_id(); + } + }; +} + +spec!(FRONTIER, FrontierSpec); +// FRONTIER_THAWING no EVM spec change +spec!(HOMESTEAD, HomesteadSpec); +// DAO_FORK no EVM spec change +spec!(TANGERINE, TangerineSpec); +spec!(SPURIOUS_DRAGON, SpuriousDragonSpec); +spec!(BYZANTIUM, ByzantiumSpec); +// CONSTANTINOPLE was overridden with PETERSBURG +spec!(PETERSBURG, PetersburgSpec); +spec!(ISTANBUL, IstanbulSpec); +// MUIR_GLACIER no EVM spec change +spec!(BERLIN, BerlinSpec); +spec!(LONDON, LondonSpec); +// ARROW_GLACIER no EVM spec change +// GRAY_GLACIER no EVM spec change +spec!(MERGE, MergeSpec); +spec!(SHANGHAI, ShanghaiSpec); +spec!(CANCUN, CancunSpec); +spec!(PRAGUE, PragueSpec); +spec!(PRAGUE_EOF, PragueEofSpec); + +spec!(LATEST, LatestSpec); + +// Optimism Hardforks +spec!(BEDROCK, BedrockSpec); +spec!(REGOLITH, RegolithSpec); +spec!(CANYON, CanyonSpec); +spec!(ECOTONE, EcotoneSpec); +spec!(FJORD, FjordSpec); + +#[macro_export] +macro_rules! optimism_spec_to_generic { + ($spec_id:expr, $e:expr) => {{ + // We are transitioning from var to generic spec. + match $spec_id { + $crate::optimism::OptimismSpecId::FRONTIER + | $crate::optimism::OptimismSpecId::FRONTIER_THAWING => { + use $crate::optimism::FrontierSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::HOMESTEAD + | $crate::optimism::OptimismSpecId::DAO_FORK => { + use $crate::optimism::HomesteadSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::TANGERINE => { + use $crate::optimism::TangerineSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::SPURIOUS_DRAGON => { + use $crate::optimism::SpuriousDragonSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::BYZANTIUM => { + use $crate::optimism::ByzantiumSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::PETERSBURG + | $crate::optimism::OptimismSpecId::CONSTANTINOPLE => { + use $crate::optimism::PetersburgSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::ISTANBUL + | $crate::optimism::OptimismSpecId::MUIR_GLACIER => { + use $crate::optimism::IstanbulSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::BERLIN => { + use $crate::optimism::BerlinSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::LONDON + | $crate::optimism::OptimismSpecId::ARROW_GLACIER + | $crate::optimism::OptimismSpecId::GRAY_GLACIER => { + use $crate::optimism::LondonSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::MERGE => { + use $crate::optimism::MergeSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::SHANGHAI => { + use $crate::optimism::ShanghaiSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::CANCUN => { + use $crate::optimism::CancunSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::LATEST => { + use $crate::optimism::LatestSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::PRAGUE => { + use $crate::optimism::PragueSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::PRAGUE_EOF => { + use $crate::optimism::PragueEofSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::BEDROCK => { + use $crate::optimism::BedrockSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::REGOLITH => { + use $crate::optimism::RegolithSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::CANYON => { + use $crate::optimism::CanyonSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::ECOTONE => { + use $crate::optimism::EcotoneSpec as SPEC; + $e + } + $crate::optimism::OptimismSpecId::FJORD => { + use $crate::optimism::FjordSpec as SPEC; + $e + } + } + }}; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn optimism_spec_to_generic() { + optimism_spec_to_generic!( + OptimismSpecId::FRONTIER, + assert_eq!(SPEC::SPEC_ID, SpecId::FRONTIER) + ); + optimism_spec_to_generic!( + OptimismSpecId::FRONTIER_THAWING, + assert_eq!(SPEC::SPEC_ID, SpecId::FRONTIER) + ); + optimism_spec_to_generic!( + OptimismSpecId::HOMESTEAD, + assert_eq!(SPEC::SPEC_ID, SpecId::HOMESTEAD) + ); + optimism_spec_to_generic!( + OptimismSpecId::DAO_FORK, + assert_eq!(SPEC::SPEC_ID, SpecId::HOMESTEAD) + ); + optimism_spec_to_generic!( + OptimismSpecId::TANGERINE, + assert_eq!(SPEC::SPEC_ID, SpecId::TANGERINE) + ); + optimism_spec_to_generic!( + OptimismSpecId::SPURIOUS_DRAGON, + assert_eq!(SPEC::SPEC_ID, SpecId::SPURIOUS_DRAGON) + ); + optimism_spec_to_generic!( + OptimismSpecId::BYZANTIUM, + assert_eq!(SPEC::SPEC_ID, SpecId::BYZANTIUM) + ); + optimism_spec_to_generic!( + OptimismSpecId::CONSTANTINOPLE, + assert_eq!(SPEC::SPEC_ID, SpecId::PETERSBURG) + ); + optimism_spec_to_generic!( + OptimismSpecId::PETERSBURG, + assert_eq!(SPEC::SPEC_ID, SpecId::PETERSBURG) + ); + optimism_spec_to_generic!( + OptimismSpecId::ISTANBUL, + assert_eq!(SPEC::SPEC_ID, SpecId::ISTANBUL) + ); + optimism_spec_to_generic!( + OptimismSpecId::MUIR_GLACIER, + assert_eq!(SPEC::SPEC_ID, SpecId::ISTANBUL) + ); + optimism_spec_to_generic!( + OptimismSpecId::BERLIN, + assert_eq!(SPEC::SPEC_ID, SpecId::BERLIN) + ); + optimism_spec_to_generic!( + OptimismSpecId::LONDON, + assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::ARROW_GLACIER, + assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::GRAY_GLACIER, + assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::MERGE, + assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) + ); + optimism_spec_to_generic!( + OptimismSpecId::BEDROCK, + assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) + ); + optimism_spec_to_generic!( + OptimismSpecId::REGOLITH, + assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) + ); + optimism_spec_to_generic!( + OptimismSpecId::SHANGHAI, + assert_eq!(SPEC::SPEC_ID, SpecId::SHANGHAI) + ); + optimism_spec_to_generic!( + OptimismSpecId::CANYON, + assert_eq!(SPEC::SPEC_ID, SpecId::SHANGHAI) + ); + optimism_spec_to_generic!( + OptimismSpecId::CANCUN, + assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) + ); + optimism_spec_to_generic!( + OptimismSpecId::ECOTONE, + assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) + ); + optimism_spec_to_generic!( + OptimismSpecId::FJORD, + assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) + ); + optimism_spec_to_generic!( + OptimismSpecId::PRAGUE, + assert_eq!(SPEC::SPEC_ID, SpecId::PRAGUE) + ); + optimism_spec_to_generic!( + OptimismSpecId::LATEST, + assert_eq!(SPEC::SPEC_ID, SpecId::LATEST) + ); + + optimism_spec_to_generic!( + OptimismSpecId::FRONTIER, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FRONTIER) + ); + optimism_spec_to_generic!( + OptimismSpecId::FRONTIER_THAWING, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FRONTIER) + ); + optimism_spec_to_generic!( + OptimismSpecId::HOMESTEAD, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::HOMESTEAD) + ); + optimism_spec_to_generic!( + OptimismSpecId::DAO_FORK, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::HOMESTEAD) + ); + optimism_spec_to_generic!( + OptimismSpecId::TANGERINE, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::TANGERINE) + ); + optimism_spec_to_generic!( + OptimismSpecId::SPURIOUS_DRAGON, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::SPURIOUS_DRAGON) + ); + optimism_spec_to_generic!( + OptimismSpecId::BYZANTIUM, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BYZANTIUM) + ); + optimism_spec_to_generic!( + OptimismSpecId::CONSTANTINOPLE, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PETERSBURG) + ); + optimism_spec_to_generic!( + OptimismSpecId::PETERSBURG, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PETERSBURG) + ); + optimism_spec_to_generic!( + OptimismSpecId::ISTANBUL, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ISTANBUL) + ); + optimism_spec_to_generic!( + OptimismSpecId::MUIR_GLACIER, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ISTANBUL) + ); + optimism_spec_to_generic!( + OptimismSpecId::BERLIN, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BERLIN) + ); + optimism_spec_to_generic!( + OptimismSpecId::LONDON, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::ARROW_GLACIER, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::GRAY_GLACIER, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) + ); + optimism_spec_to_generic!( + OptimismSpecId::MERGE, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::MERGE) + ); + optimism_spec_to_generic!( + OptimismSpecId::BEDROCK, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BEDROCK) + ); + optimism_spec_to_generic!( + OptimismSpecId::REGOLITH, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::REGOLITH) + ); + optimism_spec_to_generic!( + OptimismSpecId::SHANGHAI, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::SHANGHAI) + ); + optimism_spec_to_generic!( + OptimismSpecId::CANYON, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::CANYON) + ); + optimism_spec_to_generic!( + OptimismSpecId::CANCUN, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::CANCUN) + ); + optimism_spec_to_generic!( + OptimismSpecId::ECOTONE, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ECOTONE) + ); + optimism_spec_to_generic!( + OptimismSpecId::FJORD, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FJORD) + ); + optimism_spec_to_generic!( + OptimismSpecId::PRAGUE, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PRAGUE) + ); + optimism_spec_to_generic!( + OptimismSpecId::PRAGUE_EOF, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PRAGUE_EOF) + ); + optimism_spec_to_generic!( + OptimismSpecId::LATEST, + assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LATEST) + ); + } + + #[test] + fn test_bedrock_post_merge_hardforks() { + assert!(BedrockSpec::optimism_enabled(OptimismSpecId::MERGE)); + assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); + assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::CANCUN)); + assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::LATEST)); + assert!(BedrockSpec::optimism_enabled(OptimismSpecId::BEDROCK)); + assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + } + + #[test] + fn test_regolith_post_merge_hardforks() { + assert!(RegolithSpec::optimism_enabled(OptimismSpecId::MERGE)); + assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); + assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::CANCUN)); + assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::LATEST)); + assert!(RegolithSpec::optimism_enabled(OptimismSpecId::BEDROCK)); + assert!(RegolithSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + } + + #[test] + fn test_bedrock_post_merge_hardforks_spec_id() { + assert!(OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::MERGE + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::SHANGHAI + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::CANCUN + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::LATEST + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::BEDROCK + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::BEDROCK, + OptimismSpecId::REGOLITH + )); + } + + #[test] + fn test_regolith_post_merge_hardforks_spec_id() { + assert!(OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::MERGE + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::SHANGHAI + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::CANCUN + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::LATEST + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::BEDROCK + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::REGOLITH, + OptimismSpecId::REGOLITH + )); + } + + #[test] + fn test_canyon_post_merge_hardforks() { + assert!(CanyonSpec::optimism_enabled(OptimismSpecId::MERGE)); + assert!(CanyonSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); + assert!(!CanyonSpec::optimism_enabled(OptimismSpecId::CANCUN)); + assert!(!CanyonSpec::optimism_enabled(OptimismSpecId::LATEST)); + assert!(CanyonSpec::optimism_enabled(OptimismSpecId::BEDROCK)); + assert!(CanyonSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + assert!(CanyonSpec::optimism_enabled(OptimismSpecId::CANYON)); + } + + #[test] + fn test_canyon_post_merge_hardforks_spec_id() { + assert!(OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::MERGE + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::SHANGHAI + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::CANCUN + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::LATEST + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::BEDROCK + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::REGOLITH + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::CANYON, + OptimismSpecId::CANYON + )); + } + + #[test] + fn test_ecotone_post_merge_hardforks() { + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::MERGE)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::CANCUN)); + assert!(!EcotoneSpec::optimism_enabled(OptimismSpecId::LATEST)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::BEDROCK)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::CANYON)); + assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::ECOTONE)); + } + + #[test] + fn test_ecotone_post_merge_hardforks_spec_id() { + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::MERGE + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::SHANGHAI + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::CANCUN + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::LATEST + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::BEDROCK + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::REGOLITH + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::CANYON + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::ECOTONE, + OptimismSpecId::ECOTONE + )); + } + + #[test] + fn test_fjord_post_merge_hardforks() { + assert!(FjordSpec::optimism_enabled(OptimismSpecId::MERGE)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::CANCUN)); + assert!(!FjordSpec::optimism_enabled(OptimismSpecId::LATEST)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::BEDROCK)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::CANYON)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::ECOTONE)); + assert!(FjordSpec::optimism_enabled(OptimismSpecId::FJORD)); + } + + #[test] + fn test_fjord_post_merge_hardforks_spec_id() { + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::MERGE + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::SHANGHAI + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::CANCUN + )); + assert!(!OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::LATEST + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::BEDROCK + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::REGOLITH + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::CANYON + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::ECOTONE + )); + assert!(OptimismSpecId::enabled( + OptimismSpecId::FJORD, + OptimismSpecId::FJORD + )); + } +} diff --git a/examples/db_by_ref.rs b/examples/db_by_ref.rs index 9e3615400c..d866899d71 100644 --- a/examples/db_by_ref.rs +++ b/examples/db_by_ref.rs @@ -3,7 +3,7 @@ use revm::{ handler::register::HandleRegister, inspector_handle_register, inspectors::{NoOpInspector, TracerEip3155}, - primitives::ResultAndState, + primitives::{EthChainSpec, ResultAndState}, DatabaseCommit, DatabaseRef, Evm, }; use std::error::Error; @@ -23,8 +23,8 @@ where fn run_transaction( db: DB, ext: EXT, - register_handles_fn: HandleRegister>, -) -> anyhow::Result<(ResultAndState, DB)> { + register_handles_fn: HandleRegister>, +) -> anyhow::Result<(ResultAndState, DB)> { let mut evm = Evm::builder() .with_ref_db(db) .with_external_context(ext) @@ -38,7 +38,7 @@ fn run_transaction( fn run_transaction_and_commit_with_ext( db: DB, ext: EXT, - register_handles_fn: HandleRegister>, + register_handles_fn: HandleRegister>, ) -> anyhow::Result<()> { // To circumvent borrow checker issues, we need to move the database into the // transaction and return it after the transaction is done. diff --git a/examples/generate_block_traces.rs b/examples/generate_block_traces.rs index c3cba6ffb3..61c10c47c3 100644 --- a/examples/generate_block_traces.rs +++ b/examples/generate_block_traces.rs @@ -124,7 +124,7 @@ async fn main() -> anyhow::Result<()> { ); etx.gas_priority_fee = Some(gas_priority_fee); etx.chain_id = Some(chain_id); - etx.nonce = Some(tx.nonce.as_u64()); + etx.nonce = tx.nonce.as_u64(); if let Some(access_list) = tx.access_list { etx.access_list = access_list .0