diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 09953f65..952f4d6c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,9 +8,7 @@ on: env: CARGO_TERM_COLOR: always ETHTESTS_VERSION: v17.0 - ETHEREUM_SPEC_TESTS_URL: https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-6%40v1.0.0/fixtures_pectra-devnet-6.tar.gz - ETHEREUM_SPEC_TESTS2_URL: https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/fixtures_stable.tar.gz - ETHEREUM_SPEC_TESTS_STATIC_URL: https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/fixtures_static.tar.gz + ETHEREUM_SPEC_TESTS_URL: https://github.com/ethereum/execution-spec-tests/releases/download/v5.4.0/fixtures_stable.tar.gz jobs: unit-tests: @@ -62,26 +60,12 @@ jobs: mkdir ethereum-spec-tests tar -xzf ethereum-spec-tests.tar.gz -C ethereum-spec-tests - - name: Download Ethereum spec tests 2 fixtures - run: | - curl -L ${{ env.ETHEREUM_SPEC_TESTS2_URL }} -o ethereum-spec-tests2.tar.gz - mkdir ethereum-spec-tests2 - tar -xzf ethereum-spec-tests2.tar.gz -C ethereum-spec-tests2 - - - name: Download Ethereum spec tests fixtures static - run: | - curl -L ${{ env.ETHEREUM_SPEC_TESTS_STATIC_URL }} -o ethereum-spec-tests-static.tar.gz - mkdir ethereum-spec-tests-static - tar -xzf ethereum-spec-tests-static.tar.gz -C ethereum-spec-tests-static - - name: Run Ethereum state tests run: | cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- state -f \ ethtests/GeneralStateTests/ \ - ethereum-spec-tests-static/fixtures/state_tests/ \ ethtests/LegacyTests/Cancun/GeneralStateTests/ \ - ethereum-spec-tests/fixtures/state_tests/ \ - ethereum-spec-tests2/fixtures/state_tests/ + ethereum-spec-tests/fixtures/state_tests/ - name: Run Ethereum vm tests run: | @@ -130,20 +114,10 @@ jobs: mkdir ethereum-spec-tests tar -xzf ethereum-spec-tests.tar.gz -C ethereum-spec-tests - curl -L ${{ env.ETHEREUM_SPEC_TESTS2_URL }} -o ethereum-spec-tests2.tar.gz - mkdir ethereum-spec-tests2 - tar -xzf ethereum-spec-tests2.tar.gz -C ethereum-spec-tests2 - - curl -L ${{ env.ETHEREUM_SPEC_TESTS_STATIC_URL }} -o ethereum-spec-tests-static.tar.gz - mkdir ethereum-spec-tests-static - tar -xzf ethereum-spec-tests-static.tar.gz -C ethereum-spec-tests-static - cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- state -f \ ethtests/GeneralStateTests/ \ - ethereum-spec-tests-static/fixtures/state_tests/ \ ethtests/LegacyTests/Cancun/GeneralStateTests/ \ - ethereum-spec-tests/fixtures/state_tests/ \ - ethereum-spec-tests2/fixtures/state_tests/ + ethereum-spec-tests/fixtures/state_tests/ cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- vm -f \ ethtests/LegacyTests/Constantinople/VMTests/vmArithmeticTest \ diff --git a/evm-tests/src/assertions.rs b/evm-tests/src/assertions.rs index 76c66601..c0539069 100644 --- a/evm-tests/src/assertions.rs +++ b/evm-tests/src/assertions.rs @@ -23,8 +23,9 @@ pub fn assert_vicinity_validation( ) }); - let is_checked = - expected == "TR_TypeNotSupported" || expected == "TR_TypeNotSupportedBlob"; + let is_checked = expected == "TR_TypeNotSupported" + || expected == "TR_TypeNotSupportedBlob" + || expected == "TransactionException.TYPE_2_TX_PRE_FORK"; assert!( is_checked, "unexpected error message {expected:?} for: [{spec:?}] {name}:{i}\n{file_name:?}", @@ -231,7 +232,8 @@ pub fn check_validate_exit_reason( || exception == "TR_IntrinsicGas" || exception == "TransactionException.INTRINSIC_GAS_TOO_LOW" || exception == "IntrinsicGas" - || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.INTRINSIC_GAS_TOO_LOW"; + || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.INTRINSIC_GAS_TOO_LOW" + || exception == "TransactionException.INTRINSIC_GAS_TOO_LOW|TransactionException.INTRINSIC_GAS_BELOW_FLOOR_GAS_COST"; assert!( check_result, "unexpected exception {exception:?} for IntrinsicGas for test: [{spec:?}] {name}" @@ -324,12 +326,35 @@ pub fn check_validate_exit_reason( ); } InvalidTxReason::GasFloorMoreThanGasLimit => { - let check_result = exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; + let check_result = exception == "TransactionException.INTRINSIC_GAS_TOO_LOW" + || exception == "TransactionException.INTRINSIC_GAS_BELOW_FLOOR_GAS_COST" + || exception == "TransactionException.INTRINSIC_GAS_TOO_LOW|TransactionException.INTRINSIC_GAS_BELOW_FLOOR_GAS_COST"; assert!( check_result, "unexpected exception {exception:?} for GasFloorMoreThanGasLimit for test: [{spec:?}] {name}" ); } + InvalidTxReason::AuthorizationListNotSupportedForCreate => { + let check_result = exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; + assert!( + check_result, + "unexpected exception {exception:?} for AuthorizationListNotSupportedForCreate for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::AuthorizationListNotSupported => { + let check_result = exception == "TransactionException.TYPE_4_TX_PRE_FORK"; + assert!( + check_result, + "unexpected exception {exception:?} for AuthorizationListNotSupported for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::AccessListNotSupported => { + let check_result = exception == "TransactionException.TYPE_1_TX_PRE_FORK"; + assert!( + check_result, + "unexpected exception {exception:?} for AccessListNotSupported for test: [{spec:?}] {name}" + ); + } _ => { panic!( "unexpected exception {exception:?} for reason {reason:?} for test: [{spec:?}] {name}" @@ -353,10 +378,10 @@ pub fn assert_empty_create_caller(expect_exception: Option<&String>, name: &str) } /// Check call expected exception -pub fn assert_call_exit_exception(expect_exception: Option<&String>, name: &str) { +pub fn assert_call_exit_exception(expect_exception: Option<&String>, name: &str, spec: &Spec) { assert!( expect_exception.is_none(), - "unexpected call exception: {expect_exception:?} for test: {name}" + "unexpected call exception: {expect_exception:?} for test: {name} [{spec:?}]" ); } @@ -411,7 +436,7 @@ pub fn check_create_exit_reason( _ => { assert!( expect_exception.is_none(), - "Unexpected json-test error: {expect_exception:?}" + "Unexpected json-test error: {expect_exception:?} with reason {reason:?} for: {name}" ); } } diff --git a/evm-tests/src/state.rs b/evm-tests/src/state.rs index 16fcfcc8..2d3073f5 100644 --- a/evm-tests/src/state.rs +++ b/evm-tests/src/state.rs @@ -193,7 +193,11 @@ fn test_run(test_config: &TestConfig, test: &StateTestCase) -> TestExecutionResu access_list.clone(), authorization_list.clone(), ); - assert_call_exit_exception(state.expect_exception.as_ref(), &test_config.name); + assert_call_exit_exception( + state.expect_exception.as_ref(), + &test_config.name, + spec, + ); } else { let code = data; diff --git a/evm-tests/src/types/eip_7623.rs b/evm-tests/src/types/eip_7623.rs deleted file mode 100644 index 489246ea..00000000 --- a/evm-tests/src/types/eip_7623.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! EIP-7623 from Prague hard fork - -/// The standard cost of calldata token. -pub const STANDARD_TOKEN_COST: usize = 4; -/// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). -pub const NON_ZERO_BYTE_DATA_COST: usize = 16; -/// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). -pub const NON_ZERO_BYTE_MULTIPLIER: usize = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST; -/// The cost floor per token -pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; - -/// Retrieve the total number of tokens in calldata. -#[must_use] -pub fn get_tokens_in_calldata(input: &[u8]) -> u64 { - let zero_data_len = bytecount::count(input, 0); - let non_zero_data_len = input.len() - zero_data_len; - u64::try_from(zero_data_len + non_zero_data_len * NON_ZERO_BYTE_MULTIPLIER).unwrap() -} - -/// Calculate the transaction cost floor as specified in EIP-7623. -#[must_use] -pub const fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { - tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 -} diff --git a/evm-tests/src/types/mod.rs b/evm-tests/src/types/mod.rs index 87fd2257..369f846f 100644 --- a/evm-tests/src/types/mod.rs +++ b/evm-tests/src/types/mod.rs @@ -14,7 +14,6 @@ use std::collections::BTreeMap; pub mod account_state; pub mod blob; pub mod eip_4844; -pub mod eip_7623; pub mod eip_7702; mod info; mod json_utils; @@ -269,8 +268,10 @@ pub enum InvalidTxReason { GasPriceEip1559, AuthorizationListNotExist, AuthorizationListNotSupported, + AuthorizationListNotSupportedForCreate, InvalidAuthorizationChain, InvalidAuthorizationSignature, CreateTransaction, GasFloorMoreThanGasLimit, + AccessListNotSupported, } diff --git a/evm-tests/src/types/transaction.rs b/evm-tests/src/types/transaction.rs index 7d1a64f2..4d54863e 100644 --- a/evm-tests/src/types/transaction.rs +++ b/evm-tests/src/types/transaction.rs @@ -4,10 +4,9 @@ use crate::types::json_utils::{ deserialize_h256_from_u256_str_opt, deserialize_u256_from_str, deserialize_u256_from_str_opt, deserialize_u8_from_str_opt, deserialize_vec_of_hex, deserialize_vec_u256_from_str, }; -use crate::types::{eip_4844, eip_7623, eip_7702, InvalidTxReason, PostState, Spec}; +use crate::types::{eip_4844, eip_7702, InvalidTxReason, PostState, Spec}; use aurora_evm::backend::MemoryVicinity; use aurora_evm::executor::stack::Authorization; -use aurora_evm::gasometer; use aurora_evm::gasometer::Gasometer; use primitive_types::{H160, H256, U256}; use serde::Deserialize; @@ -122,24 +121,24 @@ impl Transaction { )) } - fn intrinsic_gas(&self, config: &aurora_evm::Config, state: &PostState) -> Option { + fn intrinsic_gas_and_gas_floor( + &self, + config: &aurora_evm::Config, + state: &PostState, + ) -> (u64, u64) { let is_contract_creation = self.to.is_none(); let data = &self.get_data(state); let access_list = self.get_access_list(state); - // EIP-7702 let authorization_list_len = self.authorization_list.as_ref().map_or(0, Vec::len); - let cost = if is_contract_creation { - gasometer::create_transaction_cost(data, &access_list) - } else { - gasometer::call_transaction_cost(data, &access_list, authorization_list_len) - }; - - let mut g = Gasometer::new(u64::MAX, config); - g.record_transaction(cost).ok()?; - - Some(g.total_used_gas()) + Gasometer::calculate_intrinsic_gas_and_gas_floor( + data, + &access_list, + authorization_list_len, + config, + is_contract_creation, + ) } /// Validate the transaction against block, payment, and EIP constraints. @@ -160,13 +159,10 @@ impl Transaction { ) -> Result, InvalidTxReason> { let gas_limit = self.get_gas_limit(state); let mut authorization_list: Vec = vec![]; - match self.intrinsic_gas(config, state) { - None => return Err(InvalidTxReason::IntrinsicGas), - Some(required_gas) => { - if gas_limit < U256::from(required_gas) { - return Err(InvalidTxReason::IntrinsicGas); - } - } + + let (intrinsic_gas, floor_gas) = self.intrinsic_gas_and_gas_floor(config, state); + if gas_limit < U256::from(intrinsic_gas) { + return Err(InvalidTxReason::IntrinsicGas); } if block_gas_limit < gas_limit { @@ -190,6 +186,10 @@ impl Transaction { return Err(InvalidTxReason::OutOfFund); } + if TxType::from_tx_bytes(&state.tx_bytes) == TxType::AccessList && *spec < Spec::Berlin { + return Err(InvalidTxReason::AccessListNotSupported); + } + // CANCUN tx validation // Presence of max_fee_per_blob_gas means that this is a blob transaction. if *spec >= Spec::Cancun { @@ -248,9 +248,6 @@ impl Transaction { if *spec >= Spec::Prague { // EIP-7623 validation - let floor_gas = eip_7623::calc_tx_floor_cost(eip_7623::get_tokens_in_calldata( - &self.get_data(state), - )); if floor_gas > gas_limit.as_u64() { return Err(InvalidTxReason::GasFloorMoreThanGasLimit); } @@ -265,6 +262,12 @@ impl Transaction { return Err(InvalidTxReason::AuthorizationListNotExist); } + // EIP-7702 - if transaction is contract creation - validation fails + if TxType::from_tx_bytes(&state.tx_bytes) == TxType::EOAAccountCode && self.to.is_none() + { + return Err(InvalidTxReason::AuthorizationListNotSupportedForCreate); + } + // Check EIP-7702 Spec validation steps: 1 and 2 // Other validation step inside EVM transact logic. for auth in &tx_authorization_list {