diff --git a/evm-tests/jsontests/src/state.rs b/evm-tests/jsontests/src/state.rs index 8651f02b..968ca81b 100644 --- a/evm-tests/jsontests/src/state.rs +++ b/evm-tests/jsontests/src/state.rs @@ -704,10 +704,10 @@ fn assert_empty_create_caller(expect_exception: &Option, name: &str) { } /// Check call expected exception -fn assert_call_exit_exception(expect_exception: &Option) { +fn assert_call_exit_exception(expect_exception: &Option, name: &str) { assert!( expect_exception.is_none(), - "unexpected call exception: {expect_exception:?}" + "unexpected call exception: {expect_exception:?} for test: {name}" ); } @@ -747,7 +747,9 @@ fn check_create_exit_reason( return true; } _ => { - panic!("unexpected error: {err:?} for exception: {exception}") + panic!( + "unexpected error: {err:?} for exception: {exception} for test: {name}" + ) } } } else { @@ -1062,6 +1064,13 @@ fn check_validate_exit_reason( "unexpected exception {exception:?} for CreateTransaction for test: [{spec:?}] {name}" ); } + InvalidTxReason::GasFloorMoreThanGasLimit => { + let check_result = exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; + assert!( + check_result, + "unexpected exception {exception:?} for GasFloorMoreThanGasLimit for test: [{spec:?}] {name}" + ); + } _ => { panic!( "unexpected exception {exception:?} for reason {reason:?} for test: [{spec:?}] {name}" @@ -1083,6 +1092,10 @@ fn test_run( let mut tests_result = TestExecutionResult::new(); let test_tx = &test.0.transaction; for (spec, states) in &test.0.post_states { + // TODOFEE + if name != "tests/shanghai/eip3860_initcode/test_initcode.py::test_contract_creating_tx[fork_Prague-state_test-max_size_ones]" { + continue; + } // Run tests for specific SPEC (Hard fork) if let Some(s) = specific_spec.as_ref() { if s != spec { @@ -1268,7 +1281,7 @@ fn test_run( access_list, authorization_list, ); - assert_call_exit_exception(&state.expect_exception); + assert_call_exit_exception(&state.expect_exception, name); } ethjson::maybe::MaybeEmpty::None => { let code = data; diff --git a/evm-tests/jsontests/src/utils.rs b/evm-tests/jsontests/src/utils.rs index 09cab99d..124cc61b 100644 --- a/evm-tests/jsontests/src/utils.rs +++ b/evm-tests/jsontests/src/utils.rs @@ -139,6 +139,32 @@ pub fn flush() { io::stdout().flush().expect("Could not flush stdout"); } +/// EIP-7623 form Prague hard fork +pub mod eip7623 { + /// 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 = input.iter().filter(|v| **v == 0).count(); + 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 fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 + } +} + /// EIP-7702 pub mod eip7702 { use super::{Digest, Keccak256, H160, H256, U256}; @@ -331,7 +357,7 @@ pub mod eip_4844 { pub mod transaction { use crate::state::TxType; - use crate::utils::eip7702; + use crate::utils::{eip7623, eip7702}; use ethjson::hash::Address; use ethjson::maybe::MaybeEmpty; use ethjson::spec::ForkSpec; @@ -439,6 +465,12 @@ pub mod transaction { } if *spec >= ForkSpec::Prague { + // EIP-7623 validation + let floor_gas = eip7623::calc_tx_floor_cost(eip7623::get_tokens_in_calldata(&*tx.data)); + if floor_gas > tx.gas_limit.into() { + return Err(InvalidTxReason::GasFloorMoreThanGasLimit); + } + // EIP-7702 - if transaction type is EOAAccountCode then // `authorization_list` must be present if TxType::from_txbytes(&tx_state.txbytes) == TxType::EOAAccountCode @@ -546,6 +578,7 @@ pub mod transaction { InvalidAuthorizationChain, InvalidAuthorizationSignature, CreateTransaction, + GasFloorMoreThanGasLimit, } }