diff --git a/.github/workflows/ethereum-tests.yml b/.github/workflows/ethereum-tests.yml index fb530c8a13..ebf7796562 100644 --- a/.github/workflows/ethereum-tests.yml +++ b/.github/workflows/ethereum-tests.yml @@ -51,7 +51,7 @@ jobs: ethtests/EIPTests/StateTests/stEOF \ tests/eof_suite/eest/state_tests \ tests/eof_suite/evmone/state_tests \ - tests/prague_suite/state_tests + # tests/prague_suite/state_tests # - name: Run EOF validation tests # run: | # cross run --target ${{matrix.target}} --profile ${{ matrix.profile }} -p revme -- eof-validation \ diff --git a/Cargo.lock b/Cargo.lock index 3d28071ff1..d806cb3f19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,18 +93,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-eip7702" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "k256", - "serde", -] - [[package]] name = "alloy-eip7702" version = "0.4.1" @@ -124,7 +112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" dependencies = [ "alloy-eip2930", - "alloy-eip7702 0.4.1", + "alloy-eip7702", "alloy-primitives", "alloy-rlp", "alloy-serde", @@ -3182,7 +3170,6 @@ name = "revm-inspector" version = "1.0.0" dependencies = [ "auto_impl", - "derive-where", "revm", "revm-database", "serde", @@ -3260,9 +3247,6 @@ dependencies = [ name = "revm-specification" version = "1.0.0" dependencies = [ - "alloy-eip2930", - "alloy-eip7702 0.1.1", - "alloy-primitives", "enumn", "revm-primitives", "serde", diff --git a/Cargo.toml b/Cargo.toml index ccf0f86cf9..d2a5340850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "crates/revm", "crates/primitives", "crates/interpreter", - "crates/inspector", "crates/precompile", "crates/database", "crates/database/interface", @@ -21,6 +20,7 @@ members = [ # variants "crates/optimism", + "crates/inspector", # utility "crates/statetest-types", diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index e1dc9003da..246cd4240a 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -17,7 +17,7 @@ pub fn run() { .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); + tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); tx.data = bytes!("8035F0CE"); }); diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index d1e5968fd7..c1a6158719 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -39,7 +39,7 @@ pub fn run() { let context = Context::builder().with_db(db).modify_tx_chained(|tx| { tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(BURNTPIX_MAIN_ADDRESS); + tx.kind = TxKind::Call(BURNTPIX_MAIN_ADDRESS); tx.data = run_call_data.clone().into(); tx.gas_limit = u64::MAX; }); diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index de55b49b36..4d3a75a8fe 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -12,7 +12,7 @@ pub fn simple_example(bytecode: Bytecode) { .modify_tx_chained(|tx| { // Execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); + tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); tx.data = bytes!("30627b7c"); tx.gas_limit = 1_000_000_000; }); diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index f53d27e8df..6363c00c3c 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -15,7 +15,7 @@ pub fn run() { .parse() .unwrap(); tx.value = U256::from(10); - tx.transact_to = TxKind::Call( + tx.kind = TxKind::Call( "0x0000000000000000000000000000000000000000" .parse() .unwrap(), diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index da4db08a61..2ad8a2a9fc 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -89,7 +89,7 @@ impl Cmd { let mut evm = MainEvm::new( Context::builder().with_db(db).modify_tx_chained(|tx| { tx.caller = CALLER; - tx.transact_to = TxKind::Call(Address::ZERO); + tx.kind = TxKind::Call(Address::ZERO); tx.data = input; tx.nonce = nonce; }), diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 9c14a3a1aa..a3d635f984 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -19,7 +19,7 @@ use revm::{ database_interface::EmptyDB, handler::EthHandler, primitives::{keccak256, Bytes, TxKind, B256}, - specification::{eip7702::AuthorizationList, hardfork::SpecId}, + specification::hardfork::SpecId, Context, DatabaseCommit, EvmCommit, MainEvm, }; use serde_json::json; @@ -329,11 +329,20 @@ pub fn execute_test_suite( .transaction .gas_price .or(unit.transaction.max_fee_per_gas) - .unwrap_or_default(); - tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; + .unwrap_or_default() + .try_into() + .unwrap_or(u128::MAX); + tx.gas_priority_fee = unit + .transaction + .max_priority_fee_per_gas + .map(|b| u128::try_from(b).expect("max priority fee less than u128::MAX")); // EIP-4844 tx.blob_hashes = unit.transaction.blob_versioned_hashes.clone(); - tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; + tx.max_fee_per_blob_gas = unit + .transaction + .max_fee_per_blob_gas + .map(|b| u128::try_from(b).expect("max fee less than u128::MAX")) + .unwrap_or(u128::MAX); // Post and execution for (spec_name, tests) in unit.post { @@ -366,7 +375,7 @@ pub fn execute_test_suite( } }; - tx.tx_type = tx_type; + tx.tx_type = tx_type as u8; tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); @@ -385,26 +394,26 @@ pub fn execute_test_suite( .access_lists .get(test.indexes.data) .and_then(Option::as_deref) - .cloned() - .unwrap_or_default() - .into(); + .map(|access_list| { + access_list + .iter() + .map(|item| (item.address, item.storage_keys.clone())) + .collect::>() + }) + .unwrap_or_default(); tx.authorization_list = unit .transaction .authorization_list - .as_ref() - .map(|auth_list| { - AuthorizationList::Recovered( - auth_list.iter().map(|auth| auth.into_recovered()).collect(), - ) - }) + .clone() + .map(|auth_list| auth_list.into_iter().map(Into::into).collect::>()) .unwrap_or_default(); let to = match unit.transaction.to { Some(add) => TxKind::Call(add), None => TxKind::Create, }; - tx.transact_to = to; + tx.kind = to; let mut cache = cache_state.clone(); cache.set_state_clear_flag(cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs new file mode 100644 index 0000000000..3378775607 --- /dev/null +++ b/crates/context/interface/src/context.rs @@ -0,0 +1,12 @@ +/// Some actions on top of context with just Getter traits would require borrowing the context +/// with a both mutable and immutable reference. +/// +/// To allow doing this action more efficiently, we introduce a new trait that does this directly. +/// +/// Used for loading access list and applying EIP-7702 authorization list. +pub trait PerformantContextAccess { + type Error; + + /// Load access list + fn load_access_list(&mut self) -> Result<(), Self::Error>; +} diff --git a/crates/context/interface/src/lib.rs b/crates/context/interface/src/lib.rs index 8176798392..c03aba433f 100644 --- a/crates/context/interface/src/lib.rs +++ b/crates/context/interface/src/lib.rs @@ -7,6 +7,7 @@ extern crate alloc as std; pub mod block; pub mod cfg; +pub mod context; pub mod errors; pub mod host; pub mod journaled_state; @@ -15,6 +16,7 @@ pub mod transaction; pub use block::{Block, BlockGetter}; pub use cfg::{Cfg, CfgGetter, CreateScheme, TransactTo}; +pub use context::PerformantContextAccess; pub use database_interface::{DBErrorMarker, Database, DatabaseGetter}; pub use errors::ErrorGetter; pub use journaled_state::{Journal, JournalDBError, JournalGetter}; diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index dafd85ccc3..6d6b8345ea 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -2,7 +2,6 @@ use crate::transaction::TransactionError; use core::fmt::{self, Debug}; use database_interface::DBErrorMarker; use primitives::{Address, Bytes, Log, U256}; -use specification::eip7702::InvalidAuthorization; use state::EvmState; use std::{boxed::Box, string::String, vec::Vec}; @@ -318,8 +317,6 @@ pub enum InvalidTransaction { AuthorizationListInvalidFields, /// Empty Authorization List is not allowed. EmptyAuthorizationList, - /// Invalid EIP-7702 Authorization List - InvalidAuthorizationList(InvalidAuthorization), /// EIP-2930 is not supported. Eip2930NotSupported, /// EIP-1559 is not supported. @@ -332,12 +329,6 @@ pub enum InvalidTransaction { impl TransactionError for InvalidTransaction {} -impl From for InvalidTransaction { - fn from(value: InvalidAuthorization) -> Self { - Self::InvalidAuthorizationList(value) - } -} - impl core::error::Error for InvalidTransaction {} impl fmt::Display for InvalidTransaction { @@ -403,7 +394,6 @@ impl fmt::Display for InvalidTransaction { Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"), Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"), Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"), - Self::InvalidAuthorizationList(i) => fmt::Display::fmt(i, f), } } } diff --git a/crates/context/interface/src/transaction.rs b/crates/context/interface/src/transaction.rs index fa69e5904f..a4790e055f 100644 --- a/crates/context/interface/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -1,30 +1,20 @@ -mod access_list; -mod common; -pub mod eip1559; -pub mod eip2930; -pub mod eip4844; -pub mod eip7702; -pub mod legacy; pub mod transaction_type; -pub use access_list::AccessListTrait; -pub use common::CommonTxFields; -pub use eip1559::{Eip1559CommonTxFields, Eip1559Tx}; -pub use eip2930::Eip2930Tx; -pub use eip4844::Eip4844Tx; -pub use eip7702::Eip7702Tx; -pub use legacy::LegacyTx; +use specification::eip4844::GAS_PER_BLOB; pub use transaction_type::TransactionType; use auto_impl::auto_impl; use core::cmp::min; use core::fmt::Debug; -use primitives::TxKind; +use primitives::{Address, Bytes, TxKind, B256, U256}; use std::boxed::Box; /// Transaction validity error types. pub trait TransactionError: Debug + core::error::Error {} +/// (Optional signer, chain id, nonce, address) +pub type AuthorizationItem = (Option
, U256, u64, Address); + /// Main Transaction trait that abstracts and specifies all transaction currently supported by Ethereum /// /// Access to any associated type is gaited behind [`tx_type`][Transaction::tx_type] function. @@ -33,126 +23,131 @@ pub trait TransactionError: Debug + core::error::Error {} /// deprecated by not returning tx_type. #[auto_impl(&, Box, Arc, Rc)] pub trait Transaction { - /// An error that occurs when validating a transaction - type TransactionError: TransactionError; - /// Transaction type - type TransactionType: Into; - /// Access list type - type AccessList: AccessListTrait; - - type Legacy: LegacyTx; - type Eip2930: Eip2930Tx; - type Eip1559: Eip1559Tx; - type Eip4844: Eip4844Tx; - type Eip7702: Eip7702Tx; - /// Returns the transaction type. /// /// Depending on this field other functions should be called. + fn tx_type(&self) -> u8; + + /// Caller aka Author aka transaction signer. /// - /// If transaction is Legacy, then [`legacy()`][Transaction::legacy] should be called. - fn tx_type(&self) -> Self::TransactionType; + /// Note : Common field for all transactions. + fn caller(&self) -> Address; - /// Returns the legacy transaction. - fn legacy(&self) -> &Self::Legacy { - unimplemented!("legacy tx not supported") - } + /// The maximum amount of gas the transaction can use. + /// + /// Note : Common field for all transactions. + fn gas_limit(&self) -> u64; - /// Returns EIP-2930 transaction. - fn eip2930(&self) -> &Self::Eip2930 { - unimplemented!("Eip2930 tx not supported") - } + /// The value sent to the receiver of [`TxKind::Call`][primitives::TxKind::Call]. + /// + /// Note : Common field for all transactions. + fn value(&self) -> U256; - /// Returns EIP-1559 transaction. - fn eip1559(&self) -> &Self::Eip1559 { - unimplemented!("Eip1559 tx not supported") - } + /// Returns the input data of the transaction. + /// + /// Note : Common field for all transactions. + fn input(&self) -> &Bytes; + + /// The nonce of the transaction. + /// + /// Note : Common field for all transactions. + fn nonce(&self) -> u64; + + /// Transaction kind. It can be Call or Create. + /// + /// Kind is applicable for: Legacy, EIP-2930, EIP-1559 + /// And is Call for EIP-4844 and EIP-7702 transactions. + fn kind(&self) -> TxKind; - /// Returns EIP-4844 transaction. - fn eip4844(&self) -> &Self::Eip4844 { - unimplemented!("Eip4844 tx not supported") + /// Chain Id is optional for legacy transactions. + /// + /// As it was introduced in EIP-155. + fn chain_id(&self) -> Option; + + /// Gas price for the transaction. + /// It is only applicable for Legacy and EIP-2930 transactions. + /// For Eip1559 it is max_fee_per_gas. + fn gas_price(&self) -> u128; + + fn access_list(&self) -> Option>; + + fn access_list_nums(&self) -> Option<(usize, usize)> { + self.access_list().map(|al| { + let mut accounts_num = 0; + let mut storage_num = 0; + for (_, storage) in al { + accounts_num += 1; + storage_num += storage.len(); + } + + (accounts_num, storage_num) + }) } + /// Returns vector of fixed size hash(32 bytes) + /// + /// Note : EIP-4844 transaction field. + fn blob_versioned_hashes(&self) -> &[B256]; + + /// Max fee per data gas + /// + /// Note : EIP-4844 transaction field. + fn max_fee_per_blob_gas(&self) -> u128; - /// Returns EIP-7702 transaction. - fn eip7702(&self) -> &Self::Eip7702 { - unimplemented!("Eip7702 tx not supported") + /// Total gas for all blobs. Max number of blocks is already checked + /// so we dont need to check for overflow. + /// + /// TODO remove this + fn total_blob_gas(&self) -> u64 { + GAS_PER_BLOB * self.blob_versioned_hashes().len() as u64 } - /// Returns common fields for all transactions. - fn common_fields(&self) -> &dyn CommonTxFields { - match self.tx_type().into() { - TransactionType::Legacy => self.legacy(), - TransactionType::Eip2930 => self.eip2930(), - TransactionType::Eip1559 => self.eip1559(), - TransactionType::Eip4844 => self.eip4844(), - TransactionType::Eip7702 => self.eip7702(), - TransactionType::Custom => unimplemented!("Custom tx not supported"), - } + /// Calculates the maximum [EIP-4844] `data_fee` of the transaction. + /// + /// This is used for ensuring that the user has at least enough funds to pay the + /// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs. + /// + /// See EIP-4844: + /// + /// + /// TODO remove it, make a utility trait. + fn calc_max_data_fee(&self) -> U256 { + let blob_gas = U256::from(self.total_blob_gas()); + let max_blob_fee = U256::from(self.max_fee_per_blob_gas()); + max_blob_fee.saturating_mul(blob_gas) } + /// Returns length of the authorization list. + /// + /// # Note + /// Transaction is considered invalid if list is empty. + fn authorization_list_len(&self) -> usize; + + /// List of authorizations, that contains the signature that authorizes this + /// caller to place the code to signer account. + /// + /// Set EOA account code for one transaction + /// + /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) + fn authorization_list(&self) -> impl Iterator; + /// Returns maximum fee that can be paid for the transaction. - fn max_fee(&self) -> u128 { - match self.tx_type().into() { - TransactionType::Legacy => self.legacy().gas_price(), - TransactionType::Eip2930 => self.eip2930().gas_price(), - TransactionType::Eip1559 => self.eip1559().max_fee_per_gas(), - TransactionType::Eip4844 => self.eip4844().max_fee_per_gas(), - TransactionType::Eip7702 => self.eip7702().max_fee_per_gas(), - TransactionType::Custom => unimplemented!("Custom tx not supported"), - } + fn max_fee_per_gas(&self) -> u128 { + self.gas_price() } + /// Maximum priority fee per gas. + fn max_priority_fee_per_gas(&self) -> Option; + /// Returns effective gas price is gas price field for Legacy and Eip2930 transaction. /// /// While for transactions after Eip1559 it is minimum of max_fee and `base + max_priority_fee`. fn effective_gas_price(&self, base_fee: u128) -> u128 { - let tx_type = self.tx_type().into(); - let (max_fee, max_priority_fee) = match tx_type { - TransactionType::Legacy => return self.legacy().gas_price(), - TransactionType::Eip2930 => return self.eip2930().gas_price(), - TransactionType::Eip1559 => ( - self.eip1559().max_fee_per_gas(), - self.eip1559().max_priority_fee_per_gas(), - ), - TransactionType::Eip4844 => ( - self.eip4844().max_fee_per_gas(), - self.eip4844().max_priority_fee_per_gas(), - ), - TransactionType::Eip7702 => ( - self.eip7702().max_fee_per_gas(), - self.eip7702().max_priority_fee_per_gas(), - ), - TransactionType::Custom => unimplemented!("Custom tx not supported"), + let max_fee = self.gas_price(); + let Some(max_priority_fee) = self.max_priority_fee_per_gas() else { + return max_fee; }; - min(max_fee, base_fee.saturating_add(max_priority_fee)) } - - /// Returns transaction kind. - fn kind(&self) -> TxKind { - let tx_type = self.tx_type().into(); - match tx_type { - TransactionType::Legacy => self.legacy().kind(), - TransactionType::Eip2930 => self.eip2930().kind(), - TransactionType::Eip1559 => self.eip1559().kind(), - TransactionType::Eip4844 => TxKind::Call(self.eip4844().destination()), - TransactionType::Eip7702 => TxKind::Call(self.eip7702().destination()), - TransactionType::Custom => unimplemented!("Custom tx not supported"), - } - } - - /// Returns access list. - fn access_list(&self) -> Option<&Self::AccessList> { - let tx_type = self.tx_type().into(); - match tx_type { - TransactionType::Legacy => None, - TransactionType::Eip2930 => Some(self.eip2930().access_list()), - TransactionType::Eip1559 => Some(self.eip1559().access_list()), - TransactionType::Eip4844 => Some(self.eip4844().access_list()), - TransactionType::Eip7702 => Some(self.eip7702().access_list()), - TransactionType::Custom => unimplemented!("Custom tx not supported"), - } - } } #[auto_impl(&, &mut, Box, Arc)] diff --git a/crates/context/interface/src/transaction/access_list.rs b/crates/context/interface/src/transaction/access_list.rs index c12b27cd17..2e9686545a 100644 --- a/crates/context/interface/src/transaction/access_list.rs +++ b/crates/context/interface/src/transaction/access_list.rs @@ -9,14 +9,14 @@ use primitives::{Address, B256}; /// /// Number of account and storage slots is used to calculate initial tx gas cost. #[auto_impl(&, Box, Arc, Rc)] -pub trait AccessListTrait: Clone { +pub trait AccessListTrait { /// Iterate over access list. - fn iter(&self) -> impl Iterator)>; + fn access_list(&self) -> impl Iterator)>; /// Returns number of account and storage slots. - fn num_account_storages(&self) -> (usize, usize) { - let storage_num = self.iter().map(|i| i.1.count()).sum(); - let account_num = self.iter().count(); + fn access_list_nums(&self) -> (usize, usize) { + let storage_num = self.access_list().map(|i| i.1.count()).sum(); + let account_num = self.access_list().count(); (account_num, storage_num) } @@ -26,7 +26,7 @@ pub trait AccessListTrait: Clone { use specification::eip2930::AccessList; impl AccessListTrait for AccessList { - fn iter(&self) -> impl Iterator)> { + fn access_list(&self) -> impl Iterator)> { self.0.iter().map(|item| { let slots = item.storage_keys.iter().copied(); (item.address, slots) diff --git a/crates/context/interface/src/transaction/eip1559.rs b/crates/context/interface/src/transaction/eip1559.rs index 00a4b233d3..c391ed047e 100644 --- a/crates/context/interface/src/transaction/eip1559.rs +++ b/crates/context/interface/src/transaction/eip1559.rs @@ -9,7 +9,7 @@ pub trait Eip1559Tx: Eip1559CommonTxFields { /// This trait is base for Eip1559, EIp4844 and Eip7702 transactions. #[auto_impl(&, Box, Arc, Rc)] -pub trait Eip1559CommonTxFields: CommonTxFields { +pub trait Eip1559CommonTxFields: CommonTxFields + AccessListTrait { /// Access list type. type AccessList: AccessListTrait; @@ -21,7 +21,4 @@ pub trait Eip1559CommonTxFields: CommonTxFields { /// Maximum priority fee per gas. fn max_priority_fee_per_gas(&self) -> u128; - - /// EIP-1559 access list. - fn access_list(&self) -> &Self::AccessList; } diff --git a/crates/context/interface/src/transaction/eip2930.rs b/crates/context/interface/src/transaction/eip2930.rs deleted file mode 100644 index ab222a0537..0000000000 --- a/crates/context/interface/src/transaction/eip2930.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::{AccessListTrait, CommonTxFields}; -use auto_impl::auto_impl; -use primitives::TxKind; - -/// EIP-2930: Optional access lists -#[auto_impl(&, Box, Arc, Rc)] -pub trait Eip2930Tx: CommonTxFields { - type AccessList: AccessListTrait; - - /// The chain ID of the chain the transaction is intended for. - fn chain_id(&self) -> u64; - - /// The gas price of the transaction. - fn gas_price(&self) -> u128; - - /// The kind of transaction. - fn kind(&self) -> TxKind; - - /// The access list of the transaction. - fn access_list(&self) -> &Self::AccessList; -} diff --git a/crates/context/interface/src/transaction/eip7702.rs b/crates/context/interface/src/transaction/eip7702.rs index 4f58b81f16..a6a6f4fac6 100644 --- a/crates/context/interface/src/transaction/eip7702.rs +++ b/crates/context/interface/src/transaction/eip7702.rs @@ -1,107 +1,3 @@ use super::Eip1559Tx; use auto_impl::auto_impl; -use primitives::Address; - -/// EIP-7702 transaction -// TODO : Set Trait for AuthorizationList. -#[auto_impl(&, Box, Arc, Rc)] -pub trait Eip7702Tx: Eip1559Tx { - /// Destination address of the call. - fn destination(&self) -> Address; - - /// Returns length of the authorization list. - /// - /// # Note - /// Transaction is considered invalid if list is empty. - fn authorization_list_len(&self) -> usize; - - /// List of authorizations, that contains the signature that authorizes this - /// caller to place the code to signer account. - /// - /// Set EOA account code for one transaction - /// - /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) - fn authorization_list_iter(&self) -> impl Iterator; -} - -/// Authorization trait. -#[auto_impl(&, Arc)] -pub trait Authorization: Clone { - /// Authority address - /// - /// # Note - /// Authority signature can be invalid, so this method returns None if the authority - /// could not be recovered. - /// - /// Valid signature Parity should be 0 or 1 and - /// signature s-value should be less than SECP256K1N_HALF. - fn authority(&self) -> Option
; - - /// Returns authorization the chain id. - fn chain_id(&self) -> u64; - - /// Returns the nonce. - /// - /// # Note - /// If nonce is not same as the nonce of the signer account, - /// the authorization is skipped. - fn nonce(&self) -> u64; - - /// Returns the address that this account is delegated to. - fn address(&self) -> Address; - - /// Returns true if the authorization is valid. - /// - /// Temporary method needed for older EIP spec and will removed in future - /// when test get updated. - fn is_invalid(&self) -> bool; -} - -// TODO : Move to default context -use specification::eip7702::RecoveredAuthorization; - -impl Authorization for RecoveredAuthorization { - /// Authority address. Obtained by recovering of the signature. - fn authority(&self) -> Option
{ - self.authority() - } - - /// Returns authorization the chain id. - fn chain_id(&self) -> u64 { - // TODO : `chain_id` is set as u64 in newest EIP-7702 spec - self.inner().chain_id().try_into().unwrap() - } - - /// Returns the nonce. - /// - /// # Note - /// If nonce is not same as the nonce of the signer account, - /// authorization is skipped and considered invalidated. - fn nonce(&self) -> u64 { - self.inner().nonce() - } - - /// Returns the address that this account should delegate to. - fn address(&self) -> Address { - *self.inner().address() - } - - /// Returns true if the authorization is valid. - /// - /// Temporary method needed for older EIP spec and will removed in future - fn is_invalid(&self) -> bool { - use specification::{eip2::SECP256K1N_HALF, eip7702::Parity}; - - // Check y_parity, Parity::Parity means that it was 0 or 1. - if !matches!(self.inner().signature().v(), Parity::Parity(_)) { - return true; - } - - // Check s-value - if self.inner().signature().s() > SECP256K1N_HALF { - return true; - } - - false - } -} +use primitives::{Address, U256}; diff --git a/crates/context/interface/src/transaction/transaction_type.rs b/crates/context/interface/src/transaction/transaction_type.rs index e3ec55910f..f4cf9a5e43 100644 --- a/crates/context/interface/src/transaction/transaction_type.rs +++ b/crates/context/interface/src/transaction/transaction_type.rs @@ -1,9 +1,10 @@ /// Transaction types of all Ethereum transaction +#[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TransactionType { /// Legacy transaction type - Legacy, + Legacy = 0, /// EIP-2930 Access List transaction type Eip2930, /// EIP-1559 Fee market change transaction type @@ -15,3 +16,34 @@ pub enum TransactionType { /// Custom type means that transaction trait was extend and have custom types Custom, } + +impl PartialEq for TransactionType { + fn eq(&self, other: &u8) -> bool { + (*self as u8) == *other + } +} + +impl PartialEq for u8 { + fn eq(&self, other: &TransactionType) -> bool { + *self == (*other as u8) + } +} + +impl From for u8 { + fn from(tx_type: TransactionType) -> u8 { + tx_type as u8 + } +} + +impl From for TransactionType { + fn from(value: u8) -> Self { + match value { + 0 => Self::Legacy, + 1 => Self::Eip2930, + 2 => Self::Eip1559, + 3 => Self::Eip4844, + 4 => Self::Eip7702, + _ => Self::Custom, + } + } +} diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 9979c5b520..91bb13de36 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -1,9 +1,10 @@ +pub mod performant_access; + use crate::{block::BlockEnv, cfg::CfgEnv, journaled_state::JournaledState, tx::TxEnv}; use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; use context_interface::{ block::BlockSetter, journaled_state::{AccountLoad, Eip7702CodeLoad}, - result::EVMError, transaction::TransactionSetter, Block, BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, Journal, JournalGetter, Transaction, TransactionGetter, @@ -519,10 +520,10 @@ where impl, CHAIN> ErrorGetter for Context { - type Error = EVMError; + type Error = DB::Error; fn take_error(&mut self) -> Result<(), Self::Error> { - core::mem::replace(&mut self.error, Ok(())).map_err(EVMError::Database) + core::mem::replace(&mut self.error, Ok(())) } } diff --git a/crates/context/src/context/performant_access.rs b/crates/context/src/context/performant_access.rs new file mode 100644 index 0000000000..2644c2bf03 --- /dev/null +++ b/crates/context/src/context/performant_access.rs @@ -0,0 +1,28 @@ +use super::Context; +use context_interface::{Block, Cfg, Database, Journal, PerformantContextAccess, Transaction}; +use primitives::U256; + +impl< + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, + JOURNAL: Journal, + CHAIN, + > PerformantContextAccess for Context +{ + type Error = ::Error; + + fn load_access_list(&mut self) -> Result<(), Self::Error> { + let Some(access_list) = self.tx.access_list() else { + return Ok(()); + }; + for access_list in access_list { + self.journaled_state.warm_account_and_storage( + *access_list.0, + access_list.1.iter().map(|i| U256::from_be_bytes(i.0)), + )?; + } + Ok(()) + } +} diff --git a/crates/context/src/tx.rs b/crates/context/src/tx.rs index 97ef568741..b6c491df6b 100644 --- a/crates/context/src/tx.rs +++ b/crates/context/src/tx.rs @@ -1,30 +1,22 @@ -use context_interface::{ - result::InvalidTransaction, - transaction::{ - eip7702::Authorization, CommonTxFields, Eip1559CommonTxFields, Eip1559Tx, Eip2930Tx, - Eip4844Tx, Eip7702Tx, LegacyTx, TransactionType, - }, - Transaction, -}; +use context_interface::transaction::AuthorizationItem; +use context_interface::Transaction; use core::fmt::Debug; use primitives::{Address, Bytes, TxKind, B256, U256}; -use specification::eip2930::AccessList; -use specification::eip7702::AuthorizationList; use std::vec::Vec; /// The transaction environment #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxEnv { - pub tx_type: TransactionType, + pub tx_type: u8, /// Caller aka Author aka transaction signer pub caller: Address, /// The gas limit of the transaction pub gas_limit: u64, /// The gas price of the transaction - pub gas_price: U256, + pub gas_price: u128, /// The destination of the transaction - pub transact_to: TxKind, + pub kind: TxKind, /// The value sent to `transact_to` pub value: U256, /// The data of the transaction @@ -47,14 +39,14 @@ pub struct TxEnv { /// Added in [EIP-2930]. /// /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 - pub access_list: AccessList, + pub access_list: Vec<(Address, Vec)>, /// The priority fee per gas /// /// Incorporated as part of the London upgrade via [EIP-1559]. /// /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 - pub gas_priority_fee: Option, + pub gas_priority_fee: Option, /// The list of blob versioned hashes /// @@ -70,7 +62,7 @@ pub struct TxEnv { /// Incorporated as part of the Cancun upgrade via [EIP-4844]. /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub max_fee_per_blob_gas: Option, + pub max_fee_per_blob_gas: u128, /// List of authorizations /// @@ -80,31 +72,39 @@ pub struct TxEnv { /// Set EOA account code for one transaction via [EIP-7702]. /// /// [EIP-7702]: https://eips.ethereum.org/EIPS/eip-7702 - pub authorization_list: AuthorizationList, + pub authorization_list: Vec, } impl Default for TxEnv { fn default() -> Self { Self { - tx_type: TransactionType::Legacy, + tx_type: 0, caller: Address::default(), gas_limit: 30_000_000, - gas_price: U256::ZERO, - transact_to: TxKind::Call(Address::default()), + gas_price: 0, + kind: TxKind::Call(Address::default()), value: U256::ZERO, data: Bytes::default(), nonce: 0, chain_id: Some(1), // Mainnet chain ID is 1 - access_list: AccessList::default(), - gas_priority_fee: Some(U256::ZERO), + access_list: Vec::new(), + gas_priority_fee: Some(0), blob_hashes: Vec::new(), - max_fee_per_blob_gas: Some(U256::ZERO), - authorization_list: AuthorizationList::default(), + max_fee_per_blob_gas: 0, + authorization_list: Vec::new(), } } } -impl CommonTxFields for TxEnv { +impl Transaction for TxEnv { + fn tx_type(&self) -> u8 { + self.tx_type + } + + fn kind(&self) -> TxKind { + self.kind + } + fn caller(&self) -> Address { self.caller } @@ -113,150 +113,55 @@ impl CommonTxFields for TxEnv { self.gas_limit } - fn value(&self) -> U256 { - self.value + fn gas_price(&self) -> u128 { + self.gas_price } - fn input(&self) -> &Bytes { - &self.data + fn value(&self) -> U256 { + self.value } fn nonce(&self) -> u64 { self.nonce } -} - -impl Eip1559CommonTxFields for TxEnv { - type AccessList = AccessList; - - fn chain_id(&self) -> u64 { - self.chain_id.unwrap_or_default() - } - - fn max_fee_per_gas(&self) -> u128 { - self.gas_price.to() - } - - fn max_priority_fee_per_gas(&self) -> u128 { - self.gas_priority_fee.unwrap_or_default().to() - } - - fn access_list(&self) -> &Self::AccessList { - &self.access_list - } -} - -impl LegacyTx for TxEnv { - fn kind(&self) -> TxKind { - self.transact_to - } fn chain_id(&self) -> Option { self.chain_id } - fn gas_price(&self) -> u128 { - self.gas_price.try_into().unwrap_or(u128::MAX) - } -} - -impl Eip2930Tx for TxEnv { - type AccessList = AccessList; - - fn access_list(&self) -> &Self::AccessList { - &self.access_list - } - - fn chain_id(&self) -> u64 { - self.chain_id.unwrap_or_default() - } - - fn gas_price(&self) -> u128 { - self.gas_price.to() + fn access_list(&self) -> Option> { + Some( + self.access_list + .iter() + .map(|(address, storage_keys)| (address, storage_keys.as_slice())), + ) } - fn kind(&self) -> TxKind { - self.transact_to - } -} - -impl Eip1559Tx for TxEnv { - fn kind(&self) -> TxKind { - self.transact_to - } -} - -impl Eip4844Tx for TxEnv { - fn destination(&self) -> Address { - match self.transact_to { - TxKind::Call(addr) => addr, - TxKind::Create => panic!("Create transaction are not allowed in Eip4844"), - } - } - - fn blob_versioned_hashes(&self) -> &[B256] { - &self.blob_hashes + fn max_fee_per_gas(&self) -> u128 { + self.gas_price } fn max_fee_per_blob_gas(&self) -> u128 { - self.max_fee_per_blob_gas.unwrap_or_default().to() - } -} - -impl Eip7702Tx for TxEnv { - fn destination(&self) -> Address { - match self.transact_to { - TxKind::Call(addr) => addr, - TxKind::Create => panic!("Create transaction are not allowed in Eip7702"), - } + self.max_fee_per_blob_gas } fn authorization_list_len(&self) -> usize { self.authorization_list.len() } - fn authorization_list_iter(&self) -> impl Iterator { - self.authorization_list.recovered_iter() - } -} - -impl Transaction for TxEnv { - type TransactionError = InvalidTransaction; - type TransactionType = TransactionType; - - type AccessList = ::AccessList; - - type Legacy = Self; - - type Eip1559 = Self; - - type Eip2930 = Self; - - type Eip4844 = Self; - - type Eip7702 = Self; - - fn tx_type(&self) -> Self::TransactionType { - self.tx_type - } - - fn legacy(&self) -> &Self::Legacy { - self - } - - fn eip2930(&self) -> &Self::Eip2930 { - self + fn authorization_list(&self) -> impl Iterator { + self.authorization_list.iter().cloned() } - fn eip1559(&self) -> &Self::Eip1559 { - self + fn input(&self) -> &Bytes { + &self.data } - fn eip4844(&self) -> &Self::Eip4844 { - self + fn blob_versioned_hashes(&self) -> &[B256] { + &self.blob_hashes } - fn eip7702(&self) -> &Self::Eip7702 { - self + fn max_priority_fee_per_gas(&self) -> Option { + self.gas_priority_fee } } diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs index e265cd0d5e..6b3bbd3e52 100644 --- a/crates/handler/src/execution.rs +++ b/crates/handler/src/execution.rs @@ -49,7 +49,7 @@ where // Make new frame action. let spec = context.cfg().spec().into(); let tx = context.tx(); - let input = tx.common_fields().input().clone(); + let input = tx.input().clone(); let init_frame: FrameInput = match tx.kind() { TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs { @@ -57,8 +57,8 @@ where gas_limit, target_address, bytecode_address: target_address, - caller: tx.common_fields().caller(), - value: CallValue::Transfer(tx.common_fields().value()), + caller: tx.caller(), + value: CallValue::Transfer(tx.value()), scheme: CallScheme::Call, is_static: false, is_eof: false, @@ -68,16 +68,16 @@ where // If first byte of data is magic 0xEF00, then it is EOFCreate. if spec.is_enabled_in(SpecId::OSAKA) && input.starts_with(&EOF_MAGIC_BYTES) { FrameInput::EOFCreate(Box::new(EOFCreateInputs::new( - tx.common_fields().caller(), - tx.common_fields().value(), + tx.caller(), + tx.value(), gas_limit, EOFCreateKind::Tx { initdata: input }, ))) } else { FrameInput::Create(Box::new(CreateInputs { - caller: tx.common_fields().caller(), + caller: tx.caller(), scheme: CreateScheme::Create, - value: tx.common_fields().value(), + value: tx.value(), init_code: input, gas_limit, })) @@ -98,7 +98,7 @@ where let refunded = gas.refunded(); // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(context.tx().common_fields().gas_limit()); + *gas = Gas::new_spent(context.tx().gas_limit()); if instruction_result.is_ok_or_revert() { gas.erase_cost(remaining); @@ -125,13 +125,21 @@ impl EthExecution { } pub trait EthExecutionContext: - TransactionGetter + ErrorGetter + BlockGetter + JournalGetter + CfgGetter + TransactionGetter + + ErrorGetter> + + BlockGetter + + JournalGetter + + CfgGetter { } impl< ERROR, - T: TransactionGetter + ErrorGetter + BlockGetter + JournalGetter + CfgGetter, + T: TransactionGetter + + ErrorGetter> + + BlockGetter + + JournalGetter + + CfgGetter, > EthExecutionContext for T { } diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 898fe17b53..8781c4e481 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -73,7 +73,7 @@ where impl EthFrame, PRECOMPILE, INSTRUCTION> where - CTX: EthFrameContext, + CTX: EthFrameContext, ERROR: EthFrameError, PRECOMPILE: PrecompileProvider, { @@ -352,7 +352,7 @@ where } // Use nonce from tx to calculate address. - let tx = context.tx().common_fields(); + let tx = context.tx(); let create_address = tx.caller().create(tx.nonce()); (input, eof, Some(create_address)) @@ -454,7 +454,7 @@ where impl Frame for EthFrame, PRECOMPILE, INSTRUCTION> where - CTX: EthFrameContext, + CTX: EthFrameContext, ERROR: EthFrameError, PRECOMPILE: PrecompileProvider, INSTRUCTION: InstructionProvider, Host = CTX>, @@ -796,20 +796,24 @@ pub fn return_eofcreate( journal.set_code(address, Bytecode::Eof(Arc::new(bytecode))); } -pub trait EthFrameContext: - TransactionGetter + Host + ErrorGetter + BlockGetter + JournalGetter + CfgGetter +pub trait EthFrameContext: + TransactionGetter + + Host + + ErrorGetter> + + BlockGetter + + JournalGetter + + CfgGetter { } impl< - ERROR, CTX: TransactionGetter - + ErrorGetter + + ErrorGetter> + BlockGetter + JournalGetter + CfgGetter + Host, - > EthFrameContext for CTX + > EthFrameContext for CTX { } diff --git a/crates/handler/src/lib.rs b/crates/handler/src/lib.rs index c7967abcbd..f3a261fa73 100644 --- a/crates/handler/src/lib.rs +++ b/crates/handler/src/lib.rs @@ -103,7 +103,7 @@ where + BlockGetter + JournalGetter + CfgGetter - + ErrorGetter + + ErrorGetter> + JournalGetter)>> + Host, ERROR: From diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index 2e95f67858..4f3b06f942 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -36,7 +36,7 @@ impl EthPostExecution { impl PostExecutionHandler for EthPostExecution where - CTX: EthPostExecutionContext, + CTX: EthPostExecutionContext, ERROR: EthPostExecutionError, HALTREASON: HaltReasonTrait, { @@ -66,7 +66,7 @@ where exec_result: &mut Self::ExecResult, ) -> Result<(), Self::Error> { let basefee = context.block().basefee() as u128; - let caller = context.tx().common_fields().caller(); + let caller = context.tx().caller(); let effective_gas_price = context.tx().effective_gas_price(basefee); let gas = exec_result.gas(); @@ -174,9 +174,9 @@ where /// Trait for post execution context. /// // TODO : Generalize FinalOutput. -pub trait EthPostExecutionContext: +pub trait EthPostExecutionContext: TransactionGetter - + ErrorGetter + + ErrorGetter> + BlockGetter + JournalGetter)>> + CfgGetter @@ -184,13 +184,12 @@ pub trait EthPostExecutionContext: } impl< - ERROR, CTX: TransactionGetter - + ErrorGetter + + ErrorGetter> + BlockGetter + JournalGetter)>> + CfgGetter, - > EthPostExecutionContext for CTX + > EthPostExecutionContext for CTX { } diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index ef44ce558a..96c853572b 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -6,10 +6,9 @@ use bytecode::Bytecode; use context_interface::{ journaled_state::Journal, result::InvalidTransaction, - transaction::{ - eip7702::Authorization, AccessListTrait, Eip4844Tx, Eip7702Tx, Transaction, TransactionType, - }, - Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, TransactionGetter, + transaction::{Transaction, TransactionType}, + Block, BlockGetter, Cfg, CfgGetter, Database, DatabaseGetter, JournalDBError, JournalGetter, + PerformantContextAccess, TransactionGetter, }; use handler_interface::PreExecutionHandler; use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, U256}; @@ -60,14 +59,15 @@ where } // Load access list - if let Some(access_list) = context.tx().access_list().cloned() { - for access_list in access_list.iter() { - context.journal().warm_account_and_storage( - access_list.0, - access_list.1.map(|i| U256::from_be_bytes(i.0)), - )?; - } - }; + context.load_access_list()?; + // if let Some(access_list) = context.tx().access_list().cloned() { + // for access_list in access_list.iter() { + // context.journal().warm_account_and_storage( + // access_list.0, + // access_list.1.map(|i| U256::from_be_bytes(i.0)), + // )?; + // } + // }; Ok(()) } @@ -88,17 +88,16 @@ where let effective_gas_price = context.tx().effective_gas_price(basefee as u128); // 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 = - (context.tx().common_fields().gas_limit() as u128).saturating_mul(effective_gas_price); + let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); // EIP-4844 - if context.tx().tx_type().into() == TransactionType::Eip4844 { - let blob_gas = context.tx().eip4844().total_blob_gas() as u128; + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = context.tx().total_blob_gas() as u128; gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); } let is_call = context.tx().kind().is_call(); - let caller = context.tx().common_fields().caller(); + let caller = context.tx().caller(); // Load caller's account. let caller_account = context.journal().load_account(caller)?.data; @@ -130,7 +129,7 @@ pub fn apply_eip7702_auth_list< ) -> Result { // Return if there is no auth list. let tx = context.tx(); - if tx.tx_type().into() != TransactionType::Eip7702 { + if tx.tx_type() != TransactionType::Eip7702 { return Ok(0); } @@ -138,17 +137,16 @@ pub fn apply_eip7702_auth_list< authority: Option
, address: Address, nonce: u64, - chain_id: u64, + chain_id: U256, } let authorization_list = tx - .eip7702() - .authorization_list_iter() + .authorization_list() .map(|a| Authorization { - authority: a.authority(), - address: a.address(), - nonce: a.nonce(), - chain_id: a.chain_id(), + authority: a.0, + chain_id: a.1, + nonce: a.2, + address: a.3, }) .collect::>(); let chain_id = context.cfg().chain_id(); @@ -162,7 +160,7 @@ pub fn apply_eip7702_auth_list< }; // 2. Verify the chain id is either 0 or the chain's current ID. - if authorization.chain_id != 0 && authorization.chain_id != chain_id { + if authorization.chain_id.is_zero() && authorization.chain_id != U256::from(chain_id) { continue; } @@ -205,12 +203,21 @@ pub fn apply_eip7702_auth_list< } pub trait EthPreExecutionContext: - TransactionGetter + BlockGetter + JournalGetter + CfgGetter + TransactionGetter + + BlockGetter + + JournalGetter + + CfgGetter + + PerformantContextAccess::Database as Database>::Error> { } -impl EthPreExecutionContext - for CTX +impl< + CTX: TransactionGetter + + BlockGetter + + JournalGetter + + CfgGetter + + PerformantContextAccess::Database as Database>::Error>, + > EthPreExecutionContext for CTX { } diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index 08d1aff5dd..8cb10be512 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -1,10 +1,7 @@ use context_interface::{ journaled_state::Journal, result::{InvalidHeader, InvalidTransaction}, - transaction::{ - eip7702::Authorization, Eip1559CommonTxFields, Eip2930Tx, Eip4844Tx, Eip7702Tx, LegacyTx, - Transaction, TransactionType, - }, + transaction::{Transaction, TransactionType}, Block, BlockGetter, Cfg, CfgGetter, JournalDBError, JournalGetter, TransactionGetter, }; use core::cmp::{self, Ordering}; @@ -63,7 +60,7 @@ where } fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let tx_caller = context.tx().common_fields().caller(); + let tx_caller = context.tx().caller(); // Load acc let account = &mut context.journal().load_account_code(tx_caller)?; @@ -144,8 +141,8 @@ where Error: From, { // Check if the transaction's chain id is correct - let common_field = context.tx().common_fields(); - let tx_type = context.tx().tx_type().into(); + let tx_type = context.tx().tx_type(); + let tx = context.tx(); let base_fee = if context.cfg().is_base_fee_check_disabled() { None @@ -153,9 +150,8 @@ where Some(context.block().basefee() as u128) }; - match tx_type { + match TransactionType::from(tx_type) { TransactionType::Legacy => { - let tx = context.tx().legacy(); // Check chain_id only if it is present in the legacy transaction. // EIP-155: Simple replay attack protection if let Some(chain_id) = tx.chain_id() { @@ -175,9 +171,8 @@ where if !spec_id.is_enabled_in(SpecId::BERLIN) { return Err(InvalidTransaction::Eip2930NotSupported.into()); } - let tx = context.tx().eip2930(); - if context.cfg().chain_id() != tx.chain_id() { + if Some(context.cfg().chain_id()) != tx.chain_id() { return Err(InvalidTransaction::InvalidChainId.into()); } @@ -192,15 +187,14 @@ where if !spec_id.is_enabled_in(SpecId::LONDON) { return Err(InvalidTransaction::Eip1559NotSupported.into()); } - let tx = context.tx().eip1559(); - if context.cfg().chain_id() != tx.chain_id() { + if Some(context.cfg().chain_id()) != tx.chain_id() { return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas(), + tx.max_priority_fee_per_gas().unwrap_or_default(), base_fee, )?; } @@ -208,15 +202,14 @@ where if !spec_id.is_enabled_in(SpecId::CANCUN) { return Err(InvalidTransaction::Eip4844NotSupported.into()); } - let tx = context.tx().eip4844(); - if context.cfg().chain_id() != tx.chain_id() { + if Some(context.cfg().chain_id()) != tx.chain_id() { return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas(), + tx.max_priority_fee_per_gas().unwrap_or_default(), base_fee, )?; @@ -231,15 +224,14 @@ where if !spec_id.is_enabled_in(SpecId::PRAGUE) { return Err(InvalidTransaction::Eip7702NotSupported.into()); } - let tx = context.tx().eip7702(); - if context.cfg().chain_id() != tx.chain_id() { + if Some(context.cfg().chain_id()) != tx.chain_id() { return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas(), + tx.max_priority_fee_per_gas().unwrap_or_default(), base_fee, )?; @@ -248,13 +240,6 @@ where if auth_list_len == 0 { return Err(InvalidTransaction::EmptyAuthorizationList.into()); } - - // TODO : Temporary here as newest EIP have removed this check. - for auth in tx.authorization_list_iter() { - if auth.is_invalid() { - return Err(InvalidTransaction::Eip7702NotSupported.into()); - } - } } TransactionType::Custom => { // Custom transaction type check is not done here. @@ -262,16 +247,15 @@ where }; // Check if gas_limit is more than block_gas_limit - if !context.cfg().is_block_gas_limit_disabled() - && common_field.gas_limit() > context.block().gas_limit() + if !context.cfg().is_block_gas_limit_disabled() && tx.gas_limit() > context.block().gas_limit() { return Err(InvalidTransaction::CallerGasLimitMoreThanBlock.into()); } // EIP-3860: Limit and meter initcode - if spec_id.is_enabled_in(SpecId::SHANGHAI) && context.tx().kind().is_create() { + if spec_id.is_enabled_in(SpecId::SHANGHAI) && tx.kind().is_create() { let max_initcode_size = context.cfg().max_code_size().saturating_mul(2); - if context.tx().common_fields().input().len() > max_initcode_size { + if context.tx().input().len() > max_initcode_size { return Err(InvalidTransaction::CreateInitCodeSizeLimit.into()); } } @@ -288,7 +272,8 @@ pub fn validate_tx_against_account( where ERROR: From, { - let tx_type = context.tx().tx_type().into(); + let tx = context.tx(); + let tx_type = context.tx().tx_type(); // EIP-3607: Reject transactions from senders with deployed code // This EIP is introduced after london but there was no collision in past // so we can leave it enabled always @@ -303,7 +288,7 @@ where // Check that the transaction's nonce is correct if !context.cfg().is_nonce_check_disabled() { - let tx = context.tx().common_fields().nonce(); + let tx = tx.nonce(); let state = account.info.nonce; match tx.cmp(&state) { Ordering::Greater => { @@ -317,13 +302,12 @@ where } // gas_limit * max_fee + value - let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) - .checked_mul(U256::from(context.tx().max_fee())) - .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) + let mut balance_check = U256::from(tx.gas_limit()) + .checked_mul(U256::from(tx.max_fee_per_gas())) + .and_then(|gas_cost| gas_cost.checked_add(tx.value())) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; if tx_type == TransactionType::Eip4844 { - let tx = context.tx().eip4844(); let data_fee = tx.calc_max_data_fee(); balance_check = balance_check .checked_add(data_fee) @@ -344,36 +328,25 @@ where } /// Validate initial transaction gas. -pub fn validate_initial_tx_gas( - env: TxGetter, - spec_id: SpecId, -) -> Result +pub fn validate_initial_tx_gas(context: CTX, spec_id: SpecId) -> Result where + CTX: TransactionGetter, Error: From, { - let tx_type = env.tx().tx_type().into(); - - let authorization_list_num = if tx_type == TransactionType::Eip7702 { - env.tx().eip7702().authorization_list_len() as u64 - } else { - 0 - }; - - let common_fields = env.tx().common_fields(); - let is_create = env.tx().kind().is_create(); - let input = common_fields.input(); - let access_list = env.tx().access_list(); + let tx = context.tx(); + let (accounts, storages) = tx.access_list_nums().unwrap_or_default(); let initial_gas_spend = gas::validate_initial_tx_gas( spec_id, - input, - is_create, - access_list, - authorization_list_num, + tx.input(), + tx.kind().is_create(), + accounts as u64, + storages as u64, + tx.authorization_list_len() as u64, ); // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > common_fields.gas_limit() { + if initial_gas_spend > tx.gas_limit() { return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); } Ok(initial_gas_spend) diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index d35174136b..c3da09b56b 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -27,7 +27,6 @@ revm.workspace = true # mics auto_impl.workspace = true -derive-where.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 188f356425..920336e231 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -1,5 +1,4 @@ use crate::{inspectors::GasInspector, Inspector}; -use derive_where::derive_where; use revm::{ bytecode::opcode::OpCode, context::Cfg, @@ -15,9 +14,7 @@ use serde::Serialize; use std::io::Write; /// [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) tracer [Inspector]. -#[derive_where(Debug; CTX, INTR)] pub struct TracerEip3155 { - #[derive_where(skip)] output: Box, gas_inspector: GasInspector, /// Print summary of the execution. @@ -173,7 +170,7 @@ where fn print_summary(&mut self, result: &InterpreterResult, context: &mut CTX) { if self.print_summary { let spec = context.cfg().spec().into(); - let gas_limit = context.tx().common_fields().gas_limit(); + let gas_limit = context.tx().gas_limit(); let value = Summary { state_root: B256::ZERO.to_string(), output: result.output.to_string(), diff --git a/crates/inspector/src/gas.rs b/crates/inspector/src/gas.rs index 303d4f52f5..c64d4189e1 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -179,7 +179,7 @@ impl GasInspector { // *tx = ::Transaction::default(); // tx.caller = address!("1000000000000000000000000000000000000000"); -// tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); +// tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); // tx.gas_limit = 21100; // }) // .append_handler_register(inspector_handle_register) diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index a9182b536d..43f85da374 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -246,7 +246,7 @@ where impl Frame for InspectorEthFrame where CTX: TransactionGetter - + ErrorGetter + + ErrorGetter> + BlockGetter + JournalGetter + CfgGetter diff --git a/crates/inspector/src/inspector_context.rs b/crates/inspector/src/inspector_context.rs index 66f1fa1d7b..fecbe42d66 100644 --- a/crates/inspector/src/inspector_context.rs +++ b/crates/inspector/src/inspector_context.rs @@ -3,7 +3,8 @@ use revm::{ block::BlockSetter, journaled_state::{AccountLoad, Eip7702CodeLoad}, transaction::TransactionSetter, - BlockGetter, CfgGetter, DatabaseGetter, ErrorGetter, JournalGetter, TransactionGetter, + BlockGetter, CfgGetter, DatabaseGetter, ErrorGetter, JournalGetter, + PerformantContextAccess, TransactionGetter, }, database_interface::Database, handler::FrameResult, @@ -36,8 +37,7 @@ where + DatabaseGetter + JournalGetter + ErrorGetter - + Host - + ErrorGetter, + + Host, { pub fn new(inner: CTX, inspector: INSP) -> Self { Self { @@ -236,7 +236,7 @@ where impl ErrorGetter for InspectorContext where - CTX: ErrorGetter + DatabaseGetter, + CTX: ErrorGetter + JournalGetter, { type Error = ::Error; @@ -295,3 +295,14 @@ where self.inner.journal_ext() } } + +impl PerformantContextAccess for InspectorContext +where + CTX: PerformantContextAccess + DatabaseGetter, +{ + type Error = ::Error; + + fn load_access_list(&mut self) -> Result<(), Self::Error> { + self.inner.load_access_list() + } +} diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 8bdee00324..c989fb0a9b 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,9 +1,6 @@ use super::constants::*; use crate::{num_words, tri, SStoreResult, SelfDestructResult, StateLoad}; -use context_interface::{ - journaled_state::{AccountLoad, Eip7702CodeLoad}, - transaction::AccessListTrait, -}; +use context_interface::journaled_state::{AccountLoad, Eip7702CodeLoad}; use primitives::U256; use specification::{eip7702, hardfork::SpecId}; @@ -350,11 +347,12 @@ pub const fn memory_gas(num_words: usize) -> u64 { /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn validate_initial_tx_gas( +pub fn validate_initial_tx_gas( spec_id: SpecId, input: &[u8], is_create: bool, - access_list: Option<&AccessListT>, + access_list_accounts: u64, + access_list_storages: u64, authorization_list_num: u64, ) -> u64 { let mut initial_gas = 0; @@ -372,11 +370,8 @@ pub fn validate_initial_tx_gas( }; // Get number of access list account and storages. - if let Some(access_list) = access_list { - let (account_num, storage_num) = access_list.num_account_storages(); - initial_gas += account_num as u64 * ACCESS_LIST_ADDRESS; - initial_gas += storage_num as u64 * ACCESS_LIST_STORAGE_KEY; - } + initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS; + initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY; // Base stipend initial_gas += if is_create { diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 42b29bc804..099d5eba8b 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -4,7 +4,7 @@ use crate::{ interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTrait}, Host, }; -use context_interface::{transaction::Eip4844Tx, Block, Transaction, TransactionType}; +use context_interface::{Block, Transaction, TransactionType}; use primitives::U256; pub fn gasprice( @@ -24,10 +24,7 @@ pub fn origin( host: &mut H, ) { gas!(interpreter, gas::BASE); - push!( - interpreter, - host.tx().common_fields().caller().into_word().into() - ); + push!(interpreter, host.tx().caller().into_word().into()); } // EIP-4844: Shard Blob Transactions @@ -40,9 +37,8 @@ pub fn blob_hash( popn_top!([], index, interpreter); let i = as_usize_saturated!(index); let tx = &host.tx(); - *index = if tx.tx_type().into() == TransactionType::Eip4844 { - tx.eip4844() - .blob_versioned_hashes() + *index = if tx.tx_type() == TransactionType::Eip4844 { + tx.blob_versioned_hashes() .get(i) .cloned() .map(|b| U256::from_be_bytes(*b)) diff --git a/crates/optimism/src/fast_lz.rs b/crates/optimism/src/fast_lz.rs index 65a23f75ef..358dfd5765 100644 --- a/crates/optimism/src/fast_lz.rs +++ b/crates/optimism/src/fast_lz.rs @@ -177,7 +177,7 @@ fn u24(input: &[u8], idx: u32) -> u32 { // panic!("Default is base tx"); // }; // tx.caller = address!("1000000000000000000000000000000000000000"); -// tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); +// tx.kind = TxKind::Call(address!("0000000000000000000000000000000000000000")); // tx.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); // tx.gas_limit = 300_000; // *enveloped_tx = Some(Bytes::default()); diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index 616dbb809c..b2d6ffbb77 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -4,16 +4,16 @@ pub mod precompiles; use crate::{ transaction::{ - abstraction::OpTxGetter, deposit::DepositTransaction, OpTransactionType, OpTxTrait, + abstraction::OpTxGetter, + deposit::{DepositTransaction, DEPOSIT_TRANSACTION_TYPE}, + OpTransactionError, OpTxTrait, }, - L1BlockInfoGetter, OpSpec, OpSpecId, OpTransactionError, OptimismHaltReason, - BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, + L1BlockInfoGetter, OpSpec, OpSpecId, OptimismHaltReason, BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, }; use precompiles::OpPrecompileProvider; use revm::{ context_interface::{ result::{ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, - transaction::CommonTxFields, Block, Cfg, CfgGetter, DatabaseGetter, Journal, Transaction, TransactionGetter, }, handler::{ @@ -55,7 +55,7 @@ where // Have Cfg with OpSpec ::Cfg: Cfg, // Have transaction with OpTransactionType - ::Transaction: Transaction, + //::Transaction: Transaction, // Add additional error type. ERROR: EthValidationError + From, { @@ -66,8 +66,8 @@ where fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. let tx_type = context.tx().tx_type(); - if tx_type == OpTransactionType::Deposit { - let tx = context.op_tx().deposit(); + if tx_type == DEPOSIT_TRANSACTION_TYPE { + let tx = context.op_tx(); // Do not allow for a system transaction to be processed if Regolith is enabled. if tx.is_system_transaction() && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { @@ -80,7 +80,7 @@ where /// Validate transactions against state. fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - if context.tx().tx_type() == OpTransactionType::Deposit { + if context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE { return Ok(()); } self.eth.validate_tx_against_state(context) @@ -100,7 +100,6 @@ impl PreExecutionHandler for OpPreExecution where CTX: EthPreExecutionContext + DatabaseGetter + OpTxGetter + L1BlockInfoGetter, ::Cfg: Cfg, - ::Transaction: Transaction, ERROR: EthPreExecutionError + From<<::Database as Database>::Error>, { type Context = CTX; @@ -109,7 +108,7 @@ where fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { // The L1-cost fee is only computed for Optimism non-deposit transactions. let spec = context.cfg().spec(); - if context.tx().tx_type() != OpTransactionType::Deposit { + if context.tx().tx_type() != DEPOSIT_TRANSACTION_TYPE { let l1_block_info: crate::L1BlockInfo = super::L1BlockInfo::try_fetch(context.db(), spec)?; @@ -125,15 +124,15 @@ where } fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let caller = context.tx().common_fields().caller(); - let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + let caller = context.tx().caller(); + let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; // 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. let mut tx_l1_cost = U256::ZERO; if is_deposit { - let tx = context.op_tx().deposit(); + let tx = context.op_tx(); if let Some(mint) = tx.mint() { let mut caller_account = context.journal().load_account(caller)?; caller_account.info.balance += U256::from(mint); @@ -187,10 +186,10 @@ pub struct OpExecution< impl ExecutionHandler for OpExecution where - CTX: EthExecutionContext + EthFrameContext + OpTxGetter, + CTX: EthExecutionContext + EthFrameContext + OpTxGetter, ERROR: EthExecutionError + EthFrameError, ::Cfg: Cfg, - ::Transaction: Transaction, + //::Transaction: Transaction, FRAME: Frame, { type Context = CTX; @@ -212,8 +211,8 @@ where mut frame_result: ::FrameResult, ) -> Result { let tx = context.tx(); - let is_deposit = tx.tx_type() == OpTransactionType::Deposit; - let tx_gas_limit = tx.common_fields().gas_limit(); + let is_deposit = tx.tx_type() == DEPOSIT_TRANSACTION_TYPE; + let tx_gas_limit = tx.gas_limit(); let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); let instruction_result = frame_result.interpreter_result().result; @@ -244,7 +243,7 @@ where gas.erase_cost(remaining); gas.record_refund(refunded); } else if is_deposit { - let tx = context.op_tx().deposit(); + let tx = context.op_tx(); if tx.is_system_transaction() { // System transactions were a special type of deposit transaction in // the Bedrock hardfork that did not incur any gas costs. @@ -282,14 +281,14 @@ pub trait IsTxError { impl PostExecutionHandler for OpPostExecution where - CTX: EthPostExecutionContext + OpTxGetter + L1BlockInfoGetter + DatabaseGetter, + CTX: EthPostExecutionContext + OpTxGetter + L1BlockInfoGetter + DatabaseGetter, ERROR: EthPostExecutionError + EthFrameError + From + FromStringError + IsTxError, ::Cfg: Cfg, - ::Transaction: Transaction, + //::Transaction: Transaction, { type Context = CTX; type Error = ERROR; @@ -304,7 +303,7 @@ where ) { exec_result.gas_mut().record_refund(eip7702_refund); - let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); // Prior to Regolith, deposit transactions did not receive gas refunds. @@ -331,7 +330,7 @@ where ) -> Result<(), Self::Error> { self.eth.reward_beneficiary(context, exec_result)?; - let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; // Transfer fee to coinbase/beneficiary. if !is_deposit { @@ -375,7 +374,7 @@ where // 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.tx().tx_type() == OpTransactionType::Deposit; + let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; if is_deposit && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { return Err(ERROR::from(OpTransactionError::HaltedDepositPostRegolith)); } @@ -394,11 +393,11 @@ where ) -> Result { //end_output - let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + let is_deposit = context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; end_output.or_else(|err| { if err.is_tx_error() && is_deposit { let spec = context.cfg().spec(); - let tx = context.op_tx().deposit(); + let tx = context.op_tx(); let caller = tx.caller(); let mint = tx.mint(); let is_system_tx = tx.is_system_transaction(); diff --git a/crates/optimism/src/lib.rs b/crates/optimism/src/lib.rs index fbf11e0da1..ada77d4fa7 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -19,4 +19,4 @@ pub use l1block::{ }; pub use result::OptimismHaltReason; pub use spec::*; -pub use transaction::{error::OpTransactionError, OpTransaction, OpTransactionType}; +pub use transaction::{error::OpTransactionError, OpTransaction}; diff --git a/crates/optimism/src/transaction.rs b/crates/optimism/src/transaction.rs index 6261ce4568..813dc425ae 100644 --- a/crates/optimism/src/transaction.rs +++ b/crates/optimism/src/transaction.rs @@ -2,4 +2,5 @@ pub mod abstraction; pub mod deposit; pub mod error; -pub use abstraction::{OpTransaction, OpTransactionType, OpTxTrait}; +pub use abstraction::{OpTransaction, OpTxTrait}; +pub use error::OpTransactionError; diff --git a/crates/optimism/src/transaction/abstraction.rs b/crates/optimism/src/transaction/abstraction.rs index 8ff2e9c5ab..bd2ecf7703 100644 --- a/crates/optimism/src/transaction/abstraction.rs +++ b/crates/optimism/src/transaction/abstraction.rs @@ -1,20 +1,15 @@ -use super::deposit::{DepositTransaction, TxDeposit}; -use crate::OpTransactionError; +use super::deposit::{DepositTransaction, DepositTransactionParts}; use revm::{ context::TxEnv, context_interface::{ - transaction::{CommonTxFields, Transaction, TransactionType}, + transaction::{AuthorizationItem, Transaction}, Journal, TransactionGetter, }, - primitives::Bytes, + primitives::{Address, Bytes, TxKind, B256, U256}, Context, Database, }; -pub trait OpTxTrait: Transaction { - type DepositTx: DepositTransaction; - - fn deposit(&self) -> &Self::DepositTx; - +pub trait OpTxTrait: Transaction + DepositTransaction { fn enveloped_tx(&self) -> Option<&Bytes>; } @@ -34,191 +29,150 @@ impl, } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum OpTransactionType { - /// Base transaction type supported on Ethereum mainnet. - Base(TransactionType), - /// Optimism-specific deposit transaction type. - Deposit, -} - -impl From for TransactionType { - fn from(tx_type: OpTransactionType) -> Self { - match tx_type { - OpTransactionType::Base(tx_type) => tx_type, - OpTransactionType::Deposit => TransactionType::Custom, - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum OpTransaction { - Base { - tx: T, - /// 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. - enveloped_tx: Option, - }, - Deposit(TxDeposit), +pub struct OpTransaction { + tx: T, + /// 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. + enveloped_tx: Option, + deposit: DepositTransactionParts, } impl Default for OpTransaction { fn default() -> Self { - Self::Base { + Self { tx: TxEnv::default(), enveloped_tx: None, + deposit: DepositTransactionParts::default(), } } } impl Transaction for OpTransaction { - // TODO - type TransactionError = OpTransactionError; - type TransactionType = OpTransactionType; + fn tx_type(&self) -> u8 { + self.tx.tx_type() + } - type AccessList = T::AccessList; + fn caller(&self) -> Address { + self.tx.caller() + } - type Legacy = T::Legacy; + fn gas_limit(&self) -> u64 { + self.tx.gas_limit() + } - type Eip2930 = T::Eip2930; + fn value(&self) -> U256 { + self.tx.value() + } - type Eip1559 = T::Eip1559; + fn input(&self) -> &Bytes { + self.tx.input() + } - type Eip4844 = T::Eip4844; + fn nonce(&self) -> u64 { + self.tx.nonce() + } - type Eip7702 = T::Eip7702; + fn kind(&self) -> TxKind { + self.tx.kind() + } - fn tx_type(&self) -> Self::TransactionType { - match self { - Self::Base { tx, .. } => OpTransactionType::Base(tx.tx_type().into()), - Self::Deposit(_) => OpTransactionType::Deposit, - } + fn chain_id(&self) -> Option { + self.tx.chain_id() } - fn common_fields(&self) -> &dyn CommonTxFields { - match self { - Self::Base { tx, .. } => tx.common_fields(), - Self::Deposit(deposit) => deposit, - } + fn access_list(&self) -> Option> { + self.tx.access_list() } - fn kind(&self) -> revm::primitives::TxKind { - match self { - Self::Base { tx, .. } => tx.kind(), - Self::Deposit(deposit) => deposit.to, - } + fn max_priority_fee_per_gas(&self) -> Option { + self.tx.max_priority_fee_per_gas() } - fn effective_gas_price(&self, base_fee: u128) -> u128 { - match self { - Self::Base { tx, .. } => tx.effective_gas_price(base_fee), - Self::Deposit(_) => base_fee, - } + fn max_fee_per_gas(&self) -> u128 { + self.tx.max_fee_per_gas() } - fn max_fee(&self) -> u128 { - match self { - Self::Base { tx, .. } => tx.max_fee(), - Self::Deposit(_) => 0, - } + fn gas_price(&self) -> u128 { + self.tx.gas_price() } - fn legacy(&self) -> &Self::Legacy { - let Self::Base { tx, .. } = self else { - panic!("Not a legacy transaction") - }; - tx.legacy() + fn blob_versioned_hashes(&self) -> &[B256] { + self.tx.blob_versioned_hashes() } - fn eip2930(&self) -> &Self::Eip2930 { - let Self::Base { tx, .. } = self else { - panic!("Not eip2930 transaction") - }; - tx.eip2930() + fn max_fee_per_blob_gas(&self) -> u128 { + self.tx.max_fee_per_blob_gas() } - fn eip1559(&self) -> &Self::Eip1559 { - let Self::Base { tx, .. } = self else { - panic!("Not a eip1559 transaction") - }; - tx.eip1559() + fn effective_gas_price(&self, base_fee: u128) -> u128 { + self.tx.effective_gas_price(base_fee) } - fn eip4844(&self) -> &Self::Eip4844 { - let Self::Base { tx, .. } = self else { - panic!("Not a eip4844 transaction") - }; - tx.eip4844() + fn authorization_list_len(&self) -> usize { + self.tx.authorization_list_len() } - fn eip7702(&self) -> &Self::Eip7702 { - let Self::Base { tx, .. } = self else { - panic!("Not a eip7702 transaction") - }; - tx.eip7702() + fn authorization_list(&self) -> impl Iterator { + self.tx.authorization_list() } } -impl OpTxTrait for OpTransaction { - type DepositTx = TxDeposit; +impl DepositTransaction for OpTransaction { + fn source_hash(&self) -> B256 { + self.deposit.source_hash + } - fn deposit(&self) -> &Self::DepositTx { - match self { - Self::Base { .. } => panic!("Not a deposit transaction"), - Self::Deposit(deposit) => deposit, - } + fn mint(&self) -> Option { + self.deposit.mint } + fn is_system_transaction(&self) -> bool { + self.deposit.is_system_transaction + } +} + +impl OpTxTrait for OpTransaction { fn enveloped_tx(&self) -> Option<&Bytes> { - match self { - Self::Base { enveloped_tx, .. } => enveloped_tx.as_ref(), - Self::Deposit(_) => None, - } + self.enveloped_tx.as_ref() } } #[cfg(test)] mod tests { - use super::*; - use revm::primitives::{Address, B256, U256}; + use crate::transaction::deposit::DEPOSIT_TRANSACTION_TYPE; - #[test] - fn test_deposit_transaction_type_conversion() { - let deposit_tx = OpTransactionType::Deposit; - let tx_type: TransactionType = deposit_tx.into(); - assert_eq!(tx_type, TransactionType::Custom); - - // Also test base transaction conversion - let base_tx = OpTransactionType::Base(TransactionType::Legacy); - let tx_type: TransactionType = base_tx.into(); - assert_eq!(tx_type, TransactionType::Legacy); - } + use super::*; + use revm::primitives::{Address, B256}; #[test] fn test_deposit_transaction_fields() { - let deposit = TxDeposit { - from: Address::ZERO, - to: revm::primitives::TxKind::Call(Address::ZERO), - value: U256::ZERO, - gas_limit: 0, - is_system_transaction: false, - mint: Some(0u128), - source_hash: B256::default(), - input: Default::default(), + let op_tx = OpTransaction { + tx: TxEnv { + tx_type: DEPOSIT_TRANSACTION_TYPE, + gas_limit: 10, + gas_price: 100, + gas_priority_fee: Some(5), + ..Default::default() + }, + enveloped_tx: None, + deposit: DepositTransactionParts { + is_system_transaction: false, + mint: Some(0u128), + source_hash: B256::default(), + }, }; - let op_tx: OpTransaction = OpTransaction::Deposit(deposit); // Verify transaction type - assert_eq!(op_tx.tx_type(), OpTransactionType::Deposit); + assert_eq!(op_tx.tx_type(), DEPOSIT_TRANSACTION_TYPE); // Verify common fields access - assert_eq!(op_tx.common_fields().gas_limit(), 0); + assert_eq!(op_tx.gas_limit(), 10); assert_eq!(op_tx.kind(), revm::primitives::TxKind::Call(Address::ZERO)); // Verify gas related calculations - assert_eq!(op_tx.effective_gas_price(100), 100); - assert_eq!(op_tx.max_fee(), 0); + assert_eq!(op_tx.effective_gas_price(90), 95); + assert_eq!(op_tx.max_fee_per_gas(), 100); } } diff --git a/crates/optimism/src/transaction/deposit.rs b/crates/optimism/src/transaction/deposit.rs index e6726733c4..0e0cff3547 100644 --- a/crates/optimism/src/transaction/deposit.rs +++ b/crates/optimism/src/transaction/deposit.rs @@ -1,77 +1,29 @@ -use revm::{ - context_interface::transaction::CommonTxFields, - primitives::{Address, Bytes, TxKind, B256, U256}, -}; +use revm::primitives::B256; -pub trait DepositTransaction: CommonTxFields { - fn source_hash(&self) -> B256; +pub const DEPOSIT_TRANSACTION_TYPE: u8 = 0x7E; - fn to(&self) -> TxKind; +pub trait DepositTransaction { + fn source_hash(&self) -> B256; fn mint(&self) -> Option; fn is_system_transaction(&self) -> bool; } -#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TxDeposit { - /// Hash that uniquely identifies the source of the deposit +pub struct DepositTransactionParts { pub source_hash: B256, - /// The address of the sender account - pub from: Address, - /// The address of the recipient account, or the null (zero-length) address if the deposited - /// transaction is a contract creation - pub to: TxKind, - /// The ETH value to mint on L2 pub mint: Option, - /// The ETH value to send to the recipient account - pub value: U256, - /// The gas limit for the L2 transaction - pub gas_limit: u64, - /// Field indicating if this transaction is exempt from the L2 gas limit pub is_system_transaction: bool, - /// Input has two uses depending if transaction is Create or Call (if `to` field is [None] - /// or [Some]) - pub input: Bytes, } -impl CommonTxFields for TxDeposit { - fn caller(&self) -> Address { - self.from - } - - fn gas_limit(&self) -> u64 { - self.gas_limit - } - - fn value(&self) -> U256 { - self.value - } - - fn input(&self) -> &Bytes { - &self.input - } - - fn nonce(&self) -> u64 { - panic!("There is no nonce in a deposit transaction"); - } -} - -impl DepositTransaction for TxDeposit { - fn source_hash(&self) -> B256 { - self.source_hash - } - - fn to(&self) -> TxKind { - self.to - } - - fn mint(&self) -> Option { - self.mint - } - - fn is_system_transaction(&self) -> bool { - self.is_system_transaction +impl DepositTransactionParts { + pub fn new(source_hash: B256, mint: Option, is_system_transaction: bool) -> Self { + Self { + source_hash, + mint, + is_system_transaction, + } } } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 797323e21a..a753fa7327 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -9,7 +9,7 @@ pub use constants::*; pub use alloy_primitives::{ self, address, b256, bytes, fixed_bytes, hex, hex_literal, keccak256, ruint, uint, Address, - Bytes, FixedBytes, Log, LogData, TxKind, B256, I256, U256, + Bytes, FixedBytes, Log, LogData, TxKind, B256, I128, I256, U128, U256, }; pub use alloy_primitives::map::{self, hash_map, hash_set, HashMap, HashSet}; diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index af925f81a2..94376074ed 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,6 +2,7 @@ use crate::{exec::EvmCommit, EvmExec}; use context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context, JournaledState}; use context_interface::{ block::BlockSetter, + context::PerformantContextAccess, journaled_state::Journal, result::{ EVMError, ExecutionResult, HaltReasonTrait, InvalidHeader, InvalidTransaction, @@ -48,13 +49,14 @@ where + JournalGetter + CfgGetter + DatabaseGetter - + ErrorGetter + + ErrorGetter> + JournalGetter< Journal: Journal< FinalOutput = (EvmState, Vec), Database = ::Database, >, - > + Host, + > + Host + + PerformantContextAccess::Database as Database>::Error>, ERROR: From + From + From> @@ -95,13 +97,14 @@ where + JournalGetter + CfgGetter + DatabaseGetter - + ErrorGetter + + ErrorGetter> + JournalGetter< Journal: Journal< FinalOutput = (EvmState, Vec), Database = ::Database, >, - > + Host, + > + Host + + PerformantContextAccess::Database as Database>::Error>, ERROR: From + From + From> @@ -153,13 +156,14 @@ where + JournalGetter + CfgGetter + DatabaseGetter - + ErrorGetter + + ErrorGetter> + JournalGetter< Journal: Journal< FinalOutput = (EvmState, Vec), Database = ::Database, >, - > + Host, + > + Host + + PerformantContextAccess::Database as Database>::Error>, ERROR: From + From + From> @@ -251,7 +255,7 @@ where // Deduce caller balance with its limit. pre_exec.deduct_caller(context)?; - let gas_limit = context.tx().common_fields().gas_limit() - initial_gas_spend; + let gas_limit = context.tx().gas_limit() - initial_gas_spend; // Apply EIP-7702 auth list. let eip7702_gas_refund = pre_exec.apply_eip7702_auth_list(context)? as i64; @@ -382,7 +386,7 @@ mod tests { )] .into(); tx.caller = caller; - tx.transact_to = TxKind::Call(auth); + tx.kind = TxKind::Call(auth); let mut tx2 = TxEnv::default(); tx2.tx_type = TransactionType::Legacy; @@ -435,7 +439,7 @@ mod tests { )] .into(); tx.caller = caller; - tx.transact_to = TxKind::Call(auth); + tx.kind = TxKind::Call(auth); }) .build(); diff --git a/crates/specification/Cargo.toml b/crates/specification/Cargo.toml index 110fd8220b..2dd2d02cd9 100644 --- a/crates/specification/Cargo.toml +++ b/crates/specification/Cargo.toml @@ -24,13 +24,6 @@ all = "warn" [dependencies] primitives = { path = "../primitives", package = "revm-primitives", version = "9.0.1", default-features = false } -# alloy -alloy-eip2930 = { version = "0.1.0", default-features = false } -alloy-eip7702 = { version = "0.1.0", default-features = false, features = ["k256"] } -alloy-primitives = { version = "0.8.2", default-features = false, features = [ - "rlp", -] } - # misc enumn = { version = "0.1" } @@ -45,6 +38,6 @@ serde = { version = "1.0", default-features = false, features = [ [features] default = ["std"] -std = ["serde?/std", "alloy-eip2930/std", "alloy-eip7702/std", "alloy-primitives/std"] -serde = ["dep:serde", "alloy-eip2930/serde", "alloy-eip7702/serde", "alloy-primitives/serde"] +std = ["serde?/std"] +serde = ["dep:serde"] serde-json = ["serde"] diff --git a/crates/specification/src/eip2930.rs b/crates/specification/src/eip2930.rs deleted file mode 100644 index 61a8485d3d..0000000000 --- a/crates/specification/src/eip2930.rs +++ /dev/null @@ -1 +0,0 @@ -pub use alloy_eip2930::{AccessList, AccessListItem}; diff --git a/crates/specification/src/eip7702.rs b/crates/specification/src/eip7702.rs index 1d0b639dc0..a129f76732 100644 --- a/crates/specification/src/eip7702.rs +++ b/crates/specification/src/eip7702.rs @@ -1,9 +1,7 @@ -mod authorization_list; -mod constants; -mod recovered_authorization; +//! EIP-7702 constants -pub use authorization_list::*; -pub use constants::*; -pub use recovered_authorization::*; +/// Base cost of updating authorized account +pub const PER_AUTH_BASE_COST: u64 = 2500; -pub use alloy_eip7702::{Authorization, SignedAuthorization}; +/// Cost of creating authorized account that was previously empty +pub const PER_EMPTY_ACCOUNT_COST: u64 = 25000; diff --git a/crates/specification/src/eip7702/authorization_list.rs b/crates/specification/src/eip7702/authorization_list.rs deleted file mode 100644 index 668aa00f47..0000000000 --- a/crates/specification/src/eip7702/authorization_list.rs +++ /dev/null @@ -1,86 +0,0 @@ -use super::RecoveredAuthorization; -use crate::eip7702::SignedAuthorization; -pub use alloy_primitives::{Parity, Signature}; -use core::fmt; -use std::{boxed::Box, vec::Vec}; - -/// Authorization list for EIP-7702 transaction type -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum AuthorizationList { - Signed(Vec), - Recovered(Vec), -} - -impl Default for AuthorizationList { - fn default() -> Self { - Self::Signed(Vec::new()) - } -} - -impl From> for AuthorizationList { - fn from(signed: Vec) -> Self { - Self::Signed(signed) - } -} - -impl From> for AuthorizationList { - fn from(recovered: Vec) -> Self { - Self::Recovered(recovered) - } -} - -impl AuthorizationList { - /// Returns length of the authorization list. - pub fn len(&self) -> usize { - match self { - Self::Signed(signed) => signed.len(), - Self::Recovered(recovered) => recovered.len(), - } - } - - /// Returns empty authorization list. - pub fn empty() -> Self { - Self::Recovered(Vec::new()) - } - - /// Returns true if the authorization list is empty. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns iterator of recovered Authorizations. - pub fn recovered_iter<'a>(&'a self) -> Box + 'a> { - match self { - Self::Signed(signed) => Box::new(signed.iter().map(|signed| signed.clone().into())), - Self::Recovered(recovered) => Box::new(recovered.clone().into_iter()), - } - } - - /// Returns recovered authorizations list. - pub fn into_recovered(self) -> Self { - let Self::Signed(signed) = self else { - return self; - }; - Self::Recovered(signed.into_iter().map(|signed| signed.into()).collect()) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InvalidAuthorization { - InvalidChainId, - InvalidYParity, - Eip2InvalidSValue, -} - -impl fmt::Display for InvalidAuthorization { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - Self::InvalidChainId => "Invalid chain_id, Expect chain's ID or zero", - Self::InvalidYParity => "Invalid y_parity, Expect 0 or 1.", - Self::Eip2InvalidSValue => "Invalid signature s-value.", - }; - f.write_str(s) - } -} diff --git a/crates/specification/src/eip7702/constants.rs b/crates/specification/src/eip7702/constants.rs deleted file mode 100644 index a129f76732..0000000000 --- a/crates/specification/src/eip7702/constants.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! EIP-7702 constants - -/// Base cost of updating authorized account -pub const PER_AUTH_BASE_COST: u64 = 2500; - -/// Cost of creating authorized account that was previously empty -pub const PER_EMPTY_ACCOUNT_COST: u64 = 25000; diff --git a/crates/specification/src/eip7702/recovered_authorization.rs b/crates/specification/src/eip7702/recovered_authorization.rs deleted file mode 100644 index e5b9a5aa55..0000000000 --- a/crates/specification/src/eip7702/recovered_authorization.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::eip7702::{Authorization, SignedAuthorization}; -use core::ops::Deref; -use primitives::Address; - -/// A recovered authorization -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct RecoveredAuthorization { - #[cfg_attr(feature = "serde", serde(flatten))] - inner: SignedAuthorization, - authority: Option
, -} - -impl RecoveredAuthorization { - /// Instantiates without performing recovery. - /// - /// This should be used carefully. - pub const fn new_unchecked(inner: SignedAuthorization, authority: Option
) -> Self { - Self { inner, authority } - } - - /// Returns a reference to the inner [`SignedAuthorization`]. - pub fn inner(&self) -> &SignedAuthorization { - &self.inner - } - - /// Get the `authority` for the authorization. - /// - /// If this is [`None`], then the authority could not be recovered. - pub const fn authority(&self) -> Option
{ - self.authority - } - - /// Splits the authorization into parts. - pub const fn into_parts(self) -> (SignedAuthorization, Option
) { - (self.inner, self.authority) - } -} - -impl From for RecoveredAuthorization { - fn from(signed_auth: SignedAuthorization) -> Self { - let authority = signed_auth.recover_authority().ok(); - Self::new_unchecked(signed_auth, authority) - } -} - -impl Deref for RecoveredAuthorization { - type Target = Authorization; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} diff --git a/crates/specification/src/lib.rs b/crates/specification/src/lib.rs index e7e0ce4d0f..6beb60d696 100644 --- a/crates/specification/src/lib.rs +++ b/crates/specification/src/lib.rs @@ -8,7 +8,6 @@ extern crate alloc as std; pub mod constants; pub mod eip170; pub mod eip2; -pub mod eip2930; pub mod eip4844; pub mod eip7702; pub mod hardfork; diff --git a/crates/statetest-types/src/test_authorization.rs b/crates/statetest-types/src/test_authorization.rs index ef9649c129..3399969964 100644 --- a/crates/statetest-types/src/test_authorization.rs +++ b/crates/statetest-types/src/test_authorization.rs @@ -1,6 +1,6 @@ use revm::{ + context_interface::transaction::AuthorizationItem, primitives::{Address, U256}, - specification::eip7702::{Authorization, Parity, RecoveredAuthorization, Signature}, }; use serde::{Deserialize, Serialize}; @@ -17,28 +17,13 @@ pub struct TestAuthorization { signer: Option
, } -impl TestAuthorization { - /// Gets the signature using the `v`, `r`, `s` values. - pub fn signature(&self) -> Signature { - let v = u64::try_from(self.v).unwrap_or(u64::MAX); - let parity = Parity::try_from(v).unwrap_or(Parity::Eip155(36)); - Signature::from_rs_and_parity(self.r, self.s, parity).unwrap() - } - - /// Converts to a recovered authorization. - pub fn into_recovered(self) -> RecoveredAuthorization { - let authorization = Authorization { - chain_id: self.chain_id, - address: self.address, - nonce: u64::try_from(self.nonce).unwrap(), - }; - let authority = self - .signature() - .recover_address_from_prehash(&authorization.signature_hash()) - .ok(); - RecoveredAuthorization::new_unchecked( - authorization.into_signed(self.signature()), - authority, +impl From for AuthorizationItem { + fn from(auth: TestAuthorization) -> AuthorizationItem { + ( + auth.signer, + auth.chain_id, + auth.nonce.try_into().unwrap_or(u64::MAX), + auth.address, ) } } diff --git a/crates/statetest-types/src/transaction.rs b/crates/statetest-types/src/transaction.rs index da5006a797..6b2bcd06f6 100644 --- a/crates/statetest-types/src/transaction.rs +++ b/crates/statetest-types/src/transaction.rs @@ -1,7 +1,6 @@ use revm::{ context_interface::transaction::TransactionType, primitives::{Address, Bytes, B256, U256}, - specification::eip2930::AccessList, }; use serde::{Deserialize, Serialize}; @@ -26,7 +25,7 @@ pub struct TransactionParts { pub max_priority_fee_per_gas: Option, #[serde(default)] - pub access_lists: Vec>, + pub access_lists: Vec>>, pub authorization_list: Option>, #[serde(default)] pub blob_versioned_hashes: Vec, @@ -75,10 +74,67 @@ impl TransactionParts { } /// Transaction part indices. -#[derive(Debug, PartialEq, Eq, Deserialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct TxPartIndices { pub data: usize, pub gas: usize, pub value: usize, } + +#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct AccessListItem { + pub address: Address, + pub storage_keys: Vec, +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn decode_tx_parts() { + let tx = r#"{ + "nonce": "0x00", + "maxPriorityFeePerGas": "0x00", + "maxFeePerGas": "0x07", + "gasLimit": [ + "0x0423ff" + ], + "to": "0x0000000000000000000000000000000000001000", + "value": [ + "0x00" + ], + "data": [ + "0x" + ], + "accessLists": [ + [ + { + "address": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + } + ] + ], + "authorizationList": [ + { + "chainId": "0x00", + "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "nonce": "0x00", + "v": "0x01", + "r": "0x5a8cac98fd240d8ef83c22db4a061ffa0facb1801245283cc05fc809d8b92837", + "s": "0x1c3162fe11d91bc24d4fa00fb19ca34531e0eacdf8142c804be44058d5b8244f", + "signer": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71" + } + ], + "sender": "0x8a0a19589531694250d570040a0c4b74576919b8", + "secretKey": "0x9e7645d0cfd9c3a04eb7a9db59a4eb7d359f2e75c9164a9d6b9a7d54e1b6a36f" + }"#; + + let _: TransactionParts = serde_json::from_str(tx).unwrap(); + } +} diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index 81a8854c1f..ddcde54d89 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -19,7 +19,7 @@ use revm::{ EthExecution, EthHandler, EthPostExecution, EthPreExecution, EthPrecompileProvider, EthValidation, }, - primitives::{TxKind, U256}, + primitives::TxKind, Context, EvmCommit, }; use std::io::BufWriter; @@ -127,19 +127,20 @@ async fn main() -> anyhow::Result<()> { evm.context.inner.modify_tx(|etx| { etx.caller = tx.from; etx.gas_limit = tx.gas_limit(); - etx.gas_price = U256::from(tx.gas_price().unwrap_or(tx.inner.max_fee_per_gas())); + etx.gas_price = tx.gas_price().unwrap_or(tx.inner.max_fee_per_gas()); etx.value = tx.value(); etx.data = tx.input().to_owned(); - etx.gas_priority_fee = tx.max_priority_fee_per_gas().map(U256::from); + etx.gas_priority_fee = tx.max_priority_fee_per_gas(); etx.chain_id = Some(chain_id); etx.nonce = tx.nonce(); - if let Some(access_list) = tx.access_list() { - etx.access_list = access_list.to_owned(); - } else { - etx.access_list = Default::default(); - } - - etx.transact_to = match tx.to() { + // TODO rakita + // if let Some(access_list) = tx.access_list() { + // etx.access_list = access_list.to_owned(); + // } else { + // etx.access_list = Default::default(); + // } + + etx.kind = match tx.to() { Some(to_address) => TxKind::Call(to_address), None => TxKind::Create, }; diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index cee79d8f2c..c8cd211f12 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -51,7 +51,7 @@ fn main() -> anyhow::Result<()> { let mut evm = MainEvm::new( Context::builder() .modify_tx_chained(|tx| { - tx.transact_to = TxKind::Create; + tx.kind = TxKind::Create; tx.data = bytecode.clone(); }) .with_db(CacheDB::::default()), @@ -70,7 +70,7 @@ fn main() -> anyhow::Result<()> { println!("Created contract at {address}"); evm.context.modify_tx(|tx| { - tx.transact_to = TxKind::Call(address); + tx.kind = TxKind::Call(address); tx.data = Default::default(); tx.nonce += 1; }); diff --git a/examples/erc20_gas/src/handlers/post_execution.rs b/examples/erc20_gas/src/handlers/post_execution.rs index d145dfe1d3..a06e7e3ef0 100644 --- a/examples/erc20_gas/src/handlers/post_execution.rs +++ b/examples/erc20_gas/src/handlers/post_execution.rs @@ -32,7 +32,7 @@ impl Default for Erc20PostExecution PostExecutionHandler for Erc20PostExecution where - CTX: EthPostExecutionContext, + CTX: EthPostExecutionContext, ERROR: EthPostExecutionError + From + From @@ -60,7 +60,7 @@ where exec_result: &mut Self::ExecResult, ) -> Result<(), Self::Error> { let basefee = context.block().basefee() as u128; - let caller = context.tx().common_fields().caller(); + let caller = context.tx().caller(); let effective_gas_price = context.tx().effective_gas_price(basefee); let gas = exec_result.gas(); diff --git a/examples/erc20_gas/src/handlers/pre_execution.rs b/examples/erc20_gas/src/handlers/pre_execution.rs index b9cfc3c87c..eef01ee7b1 100644 --- a/examples/erc20_gas/src/handlers/pre_execution.rs +++ b/examples/erc20_gas/src/handlers/pre_execution.rs @@ -1,8 +1,7 @@ use crate::{token_operation, TREASURY}; use revm::{ context_interface::{ - result::InvalidHeader, transaction::Eip4844Tx, Block, Transaction, TransactionGetter, - TransactionType, + result::InvalidHeader, Block, Transaction, TransactionGetter, TransactionType, }, handler::{EthPreExecution, EthPreExecutionContext, EthPreExecutionError}, handler_interface::PreExecutionHandler, @@ -49,15 +48,14 @@ where let blob_price = context.block().blob_gasprice().unwrap_or_default(); let effective_gas_price = context.tx().effective_gas_price(basefee); - let mut gas_cost = - (context.tx().common_fields().gas_limit() as u128).saturating_mul(effective_gas_price); + let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price); - if context.tx().tx_type().into() == TransactionType::Eip4844 { - let blob_gas = context.tx().eip4844().total_blob_gas() as u128; + if context.tx().tx_type() == TransactionType::Eip4844 { + let blob_gas = context.tx().total_blob_gas() as u128; gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); } - let caller = context.tx().common_fields().caller(); + let caller = context.tx().caller(); token_operation::(context, caller, TREASURY, U256::from(gas_cost))?; Ok(()) diff --git a/examples/erc20_gas/src/handlers/validation.rs b/examples/erc20_gas/src/handlers/validation.rs index 3d7e64d34d..1ff07c8a6b 100644 --- a/examples/erc20_gas/src/handlers/validation.rs +++ b/examples/erc20_gas/src/handlers/validation.rs @@ -3,8 +3,7 @@ use alloy_sol_types::SolValue; use revm::{ context::Cfg, context_interface::{ - result::InvalidTransaction, transaction::Eip4844Tx, Journal, Transaction, - TransactionGetter, TransactionType, + result::InvalidTransaction, Journal, Transaction, TransactionGetter, TransactionType, }, handler::{EthValidation, EthValidationContext, EthValidationError}, handler_interface::ValidationHandler, @@ -43,12 +42,12 @@ where } fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { - let caller = context.tx().common_fields().caller(); + let caller = context.tx().caller(); let caller_nonce = context.journal().load_account(caller)?.data.info.nonce; let token_account = context.journal().load_account(TOKEN)?.data.clone(); if !context.cfg().is_nonce_check_disabled() { - let tx_nonce = context.tx().common_fields().nonce(); + let tx_nonce = context.tx().nonce(); let state_nonce = caller_nonce; match tx_nonce.cmp(&state_nonce) { Ordering::Less => { @@ -67,13 +66,13 @@ where } } - let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) - .checked_mul(U256::from(context.tx().max_fee())) - .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) + let mut balance_check = U256::from(context.tx().gas_limit()) + .checked_mul(U256::from(context.tx().max_fee_per_gas())) + .and_then(|gas_cost| gas_cost.checked_add(context.tx().value())) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - if context.tx().tx_type().into() == TransactionType::Eip4844 { - let tx = context.tx().eip4844(); + if context.tx().tx_type() == TransactionType::Eip4844 { + let tx = context.tx(); let data_fee = tx.calc_max_data_fee(); balance_check = balance_check .checked_add(data_fee) diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 126f301f30..ddb372bd68 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -146,7 +146,7 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> .modify_tx_chained(|tx| { // 0x1 because calling USDC proxy from zero address fails tx.caller = address!("0000000000000000000000000000000000000001"); - tx.transact_to = TxKind::Call(token); + tx.kind = TxKind::Call(token); tx.data = encoded.into(); tx.value = U256::from(0); }), @@ -185,7 +185,7 @@ fn transfer( .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = from; - tx.transact_to = TxKind::Call(token); + tx.kind = TxKind::Call(token); tx.data = encoded.into(); tx.value = U256::from(0); }), diff --git a/examples/uniswap_get_reserves/src/main.rs b/examples/uniswap_get_reserves/src/main.rs index 50344476cd..7ab56c7c79 100644 --- a/examples/uniswap_get_reserves/src/main.rs +++ b/examples/uniswap_get_reserves/src/main.rs @@ -79,7 +79,7 @@ async fn main() -> anyhow::Result<()> { // change that to whatever caller you want to be tx.caller = address!("0000000000000000000000000000000000000000"); // account you want to transact with - tx.transact_to = TxKind::Call(pool_address); + tx.kind = TxKind::Call(pool_address); // calldata formed via abigen tx.data = encoded.into(); // transaction value in wei diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index afdcfe0857..fd97dc1f8f 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -103,7 +103,7 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> .modify_tx_chained(|tx| { // 0x1 because calling USDC proxy from zero address fails tx.caller = address!("0000000000000000000000000000000000000001"); - tx.transact_to = TxKind::Call(token); + tx.kind = TxKind::Call(token); tx.data = encoded.into(); tx.value = U256::from(0); }), @@ -149,7 +149,7 @@ async fn get_amount_out( .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = address!("0000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(uniswap_v2_router); + tx.kind = TxKind::Call(uniswap_v2_router); tx.data = encoded.into(); tx.value = U256::from(0); }), @@ -184,7 +184,7 @@ fn get_reserves(pair_address: Address, cache_db: &mut AlloyCacheDB) -> Result<(U .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = address!("0000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(pair_address); + tx.kind = TxKind::Call(pair_address); tx.data = encoded.into(); tx.value = U256::from(0); }), @@ -235,7 +235,7 @@ fn swap( .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = from; - tx.transact_to = TxKind::Call(pool_address); + tx.kind = TxKind::Call(pool_address); tx.data = encoded.into(); tx.value = U256::from(0); tx.nonce = 1; @@ -271,7 +271,7 @@ fn transfer( .with_db(cache_db) .modify_tx_chained(|tx| { tx.caller = from; - tx.transact_to = TxKind::Call(token); + tx.kind = TxKind::Call(token); tx.data = encoded.into(); tx.value = U256::from(0); }),