diff --git a/test/blockchaintest/blockchaintest_runner.cpp b/test/blockchaintest/blockchaintest_runner.cpp index 1447cc0226..1908672191 100644 --- a/test/blockchaintest/blockchaintest_runner.cpp +++ b/test/blockchaintest/blockchaintest_runner.cpp @@ -37,7 +37,7 @@ std::error_code validate_block(evmc_revision rev, state::BlobParams blob_params, // Fail if parent header was not found: the block references a parent that is neither the // genesis nor any previously-accepted block (an unknown or rejected parent). if (parent_header == nullptr) - return make_error_code(INVALID_BLOCK_PARENT); + return make_error_code(UNKNOWN_PARENT); if (test_block.block_info.number != parent_header->block_number + 1) return make_error_code(INVALID_BLOCK_NUMBER); diff --git a/test/integration/statetest/CMakeLists.txt b/test/integration/statetest/CMakeLists.txt index e18125b287..1548a6c7fa 100644 --- a/test/integration/statetest/CMakeLists.txt +++ b/test/integration/statetest/CMakeLists.txt @@ -113,7 +113,7 @@ add_test( ) set_tests_properties( ${PREFIX}/tx_invalid_nonce PROPERTIES - PASS_REGULAR_EXPRESSION "unexpected invalid transaction: nonce too high" + PASS_REGULAR_EXPRESSION "unexpected invalid transaction: TransactionException.NONCE_MISMATCH_TOO_HIGH" ) add_test( @@ -122,7 +122,7 @@ add_test( ) set_tests_properties( ${PREFIX}/tx_invalid_signature PROPERTIES - PASS_REGULAR_EXPRESSION "unexpected invalid transaction: invalid transaction signature" + PASS_REGULAR_EXPRESSION "unexpected invalid transaction: TransactionException.INVALID_SIGNATURE_VRS" ) add_test( diff --git a/test/integration/t8n/CMakeLists.txt b/test/integration/t8n/CMakeLists.txt index d658e14573..ae83fe2aeb 100644 --- a/test/integration/t8n/CMakeLists.txt +++ b/test/integration/t8n/CMakeLists.txt @@ -97,7 +97,7 @@ add_test( string( JOIN ".*" EXPECTED_OUT # Create blob transaction should be rejected: - [=["error": "blob transaction must not be a create transaction"]=] + [=["error": "TransactionException.TYPE_3_TX_CONTRACT_CREATION"]=] ) set_tests_properties( ${PREFIX}/${TEST_CASE}/out.json PROPERTIES diff --git a/test/state/errors.hpp b/test/state/errors.hpp index 3054fac14b..2a66edd80b 100644 --- a/test/state/errors.hpp +++ b/test/state/errors.hpp @@ -9,28 +9,34 @@ namespace evmone::state { +/// The reasons a transaction or a block is rejected. +/// +/// The message of each is the execution-spec-tests exception name for the same rule, so a test can +/// compare a rejection against the `expectException` its fixture states. Where the specs name more +/// than one exception for a rule, the message is the canonical one and the test harness carries the +/// alternatives; the few rules the specs do not name at all keep a plain message. enum ErrorCode : int // NOLINT(*-use-enum-class) { SUCCESS = 0, INTRINSIC_GAS_TOO_LOW, - TX_TYPE_NOT_SUPPORTED, - INSUFFICIENT_FUNDS, - NONCE_HAS_MAX_VALUE, + TYPE_NOT_SUPPORTED, + INSUFFICIENT_ACCOUNT_FUNDS, + NONCE_IS_MAX, NONCE_TOO_HIGH, NONCE_TOO_LOW, - TIP_GT_FEE_CAP, - FEE_CAP_LESS_THAN_BLOCKS, - BLOB_FEE_CAP_LESS_THAN_BLOCKS, - GAS_LIMIT_REACHED, + PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS, + INSUFFICIENT_MAX_FEE_PER_GAS, + INSUFFICIENT_MAX_FEE_PER_BLOB_GAS, + GAS_ALLOWANCE_EXCEEDED, SENDER_NOT_EOA, - INIT_CODE_SIZE_LIMIT_EXCEEDED, + INITCODE_SIZE_EXCEEDED, CREATE_BLOB_TX, EMPTY_BLOB_HASHES_LIST, INVALID_BLOB_HASH_VERSION, BLOB_GAS_LIMIT_EXCEEDED, CREATE_SET_CODE_TX, EMPTY_AUTHORIZATION_LIST, - MAX_GAS_LIMIT_EXCEEDED, + GAS_LIMIT_EXCEEDS_MAXIMUM, INVALID_CHAIN_ID, INVALID_ENCODING, INVALID_SIGNATURE, @@ -43,7 +49,7 @@ enum ErrorCode : int // NOLINT(*-use-enum-class) INCORRECT_EXCESS_BLOB_GAS, RLP_BLOCK_LIMIT_EXCEEDED, INVALID_BLOCK_TIMESTAMP_OLDER_THAN_PARENT, - INVALID_BLOCK_PARENT, + UNKNOWN_PARENT, INVALID_BLOCK_NUMBER, // Block requests collection (EIP-7685). @@ -66,51 +72,53 @@ inline const std::error_category& evmone_category() noexcept case SUCCESS: return ""; case INTRINSIC_GAS_TOO_LOW: - return "intrinsic gas too low"; - case TX_TYPE_NOT_SUPPORTED: - return "transaction type not supported"; - case INSUFFICIENT_FUNDS: - return "insufficient funds for gas * price + value"; - case NONCE_HAS_MAX_VALUE: - return "nonce has max value:"; + return "TransactionException.INTRINSIC_GAS_TOO_LOW"; + case TYPE_NOT_SUPPORTED: + return "TransactionException.TYPE_NOT_SUPPORTED"; + case INSUFFICIENT_ACCOUNT_FUNDS: + return "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS"; + case NONCE_IS_MAX: + return "TransactionException.NONCE_IS_MAX"; case NONCE_TOO_HIGH: - return "nonce too high"; + return "TransactionException.NONCE_MISMATCH_TOO_HIGH"; case NONCE_TOO_LOW: - return "nonce too low"; - case TIP_GT_FEE_CAP: - return "max priority fee per gas higher than max fee per gas"; - case FEE_CAP_LESS_THAN_BLOCKS: - return "max fee per gas less than block base fee"; - case BLOB_FEE_CAP_LESS_THAN_BLOCKS: - return "max blob fee per gas less than block base fee"; - case GAS_LIMIT_REACHED: - return "gas limit reached"; + return "TransactionException.NONCE_MISMATCH_TOO_LOW"; + case PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS: + return "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS"; + case INSUFFICIENT_MAX_FEE_PER_GAS: + return "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"; + case INSUFFICIENT_MAX_FEE_PER_BLOB_GAS: + return "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS"; + case GAS_ALLOWANCE_EXCEEDED: + return "TransactionException.GAS_ALLOWANCE_EXCEEDED"; case SENDER_NOT_EOA: - return "sender not an eoa:"; - case INIT_CODE_SIZE_LIMIT_EXCEEDED: - return "max initcode size exceeded"; + return "TransactionException.SENDER_NOT_EOA"; + case INITCODE_SIZE_EXCEEDED: + return "TransactionException.INITCODE_SIZE_EXCEEDED"; case CREATE_BLOB_TX: - return "blob transaction must not be a create transaction"; + return "TransactionException.TYPE_3_TX_CONTRACT_CREATION"; case EMPTY_BLOB_HASHES_LIST: - return "empty blob hashes list"; + return "TransactionException.TYPE_3_TX_ZERO_BLOBS"; case INVALID_BLOB_HASH_VERSION: - return "invalid blob hash version"; + return "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH"; case BLOB_GAS_LIMIT_EXCEEDED: - return "blob gas limit exceeded"; + return "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED"; case CREATE_SET_CODE_TX: - return "set code transaction must not be a create transaction"; + return "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; case EMPTY_AUTHORIZATION_LIST: - return "empty authorization list"; - case MAX_GAS_LIMIT_EXCEEDED: - return "max gas limit exceeded"; + return "TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST"; + case GAS_LIMIT_EXCEEDS_MAXIMUM: + return "TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM"; case INVALID_CHAIN_ID: - return "invalid transaction chain id"; + return "TransactionException.INVALID_CHAINID"; case INVALID_ENCODING: + // The execution specs name every way an encoding can be malformed separately + // (RLP_*), so there is no single constant standing for this one. return "invalid transaction encoding"; case INVALID_SIGNATURE: - return "invalid transaction signature"; + return "TransactionException.INVALID_SIGNATURE_VRS"; case UNKNOWN_ERROR: - return "Unknown error"; + return "unknown error"; case INCORRECT_BLOCK_FORMAT: return "BlockException.INCORRECT_BLOCK_FORMAT"; case INVALID_GASLIMIT: @@ -123,8 +131,8 @@ inline const std::error_category& evmone_category() noexcept return "BlockException.RLP_BLOCK_LIMIT_EXCEEDED"; case INVALID_BLOCK_TIMESTAMP_OLDER_THAN_PARENT: return "BlockException.INVALID_BLOCK_TIMESTAMP_OLDER_THAN_PARENT"; - case INVALID_BLOCK_PARENT: - return "BlockException.INVALID_BLOCK_PARENT"; + case UNKNOWN_PARENT: + return "BlockException.UNKNOWN_PARENT"; case INVALID_BLOCK_NUMBER: return "BlockException.INVALID_BLOCK_NUMBER"; case INVALID_DEPOSIT_EVENT_LAYOUT: diff --git a/test/state/state.cpp b/test/state/state.cpp index 44f04ca1e1..97befc4445 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -445,7 +445,7 @@ std::variant validate_transaction( { case Transaction::Type::blob: if (rev < EVMC_CANCUN) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + return make_error_code(TYPE_NOT_SUPPORTED); if (!tx.to.has_value()) return make_error_code(CREATE_BLOB_TX); if (tx.blob_hashes.empty()) @@ -455,7 +455,7 @@ std::variant validate_transaction( assert(block.blob_base_fee.has_value()); if (tx.max_blob_gas_price < *block.blob_base_fee) - return make_error_code(BLOB_FEE_CAP_LESS_THAN_BLOCKS); + return make_error_code(INSUFFICIENT_MAX_FEE_PER_BLOB_GAS); if (std::ranges::any_of(tx.blob_hashes, [](const auto& h) { return h.bytes[0] != 0x01; })) return make_error_code(INVALID_BLOB_HASH_VERSION); @@ -465,7 +465,7 @@ std::variant validate_transaction( case Transaction::Type::set_code: if (rev < EVMC_PRAGUE) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + return make_error_code(TYPE_NOT_SUPPORTED); if (!tx.to.has_value()) return make_error_code(CREATE_SET_CODE_TX); if (tx.authorization_list.empty()) @@ -481,15 +481,15 @@ std::variant validate_transaction( case Transaction::Type::blob: case Transaction::Type::eip1559: if (rev < EVMC_LONDON) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + return make_error_code(TYPE_NOT_SUPPORTED); if (tx.max_priority_gas_price > tx.max_gas_price) - return make_error_code(TIP_GT_FEE_CAP); // Priority gas price is too high. + return make_error_code(PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS); [[fallthrough]]; case Transaction::Type::access_list: if (rev < EVMC_BERLIN) - return make_error_code(TX_TYPE_NOT_SUPPORTED); + return make_error_code(TYPE_NOT_SUPPORTED); [[fallthrough]]; case Transaction::Type::legacy:; @@ -498,13 +498,13 @@ std::variant validate_transaction( assert(tx.max_priority_gas_price <= tx.max_gas_price); if (rev >= EVMC_OSAKA && tx.gas_limit > MAX_TX_GAS_LIMIT) - return make_error_code(MAX_GAS_LIMIT_EXCEEDED); + return make_error_code(GAS_LIMIT_EXCEEDS_MAXIMUM); if (tx.gas_limit > block_gas_left) - return make_error_code(GAS_LIMIT_REACHED); + return make_error_code(GAS_ALLOWANCE_EXCEEDED); if (tx.max_gas_price < block.base_fee) - return make_error_code(FEE_CAP_LESS_THAN_BLOCKS); + return make_error_code(INSUFFICIENT_MAX_FEE_PER_GAS); // We need some information about the sender so lookup the account in the state. // TODO: During transaction execution this account will be also needed, so we may pass it along. @@ -516,7 +516,7 @@ std::variant validate_transaction( return make_error_code(SENDER_NOT_EOA); // Origin must not be a contract (EIP-3607). if (sender_acc.nonce == MAX_NONCE) // Nonce value limit (EIP-2681). - return make_error_code(NONCE_HAS_MAX_VALUE); + return make_error_code(NONCE_IS_MAX); if (sender_acc.nonce < tx.nonce) return make_error_code(NONCE_TOO_HIGH); @@ -526,7 +526,7 @@ std::variant validate_transaction( // initcode size is limited by EIP-3860. if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > MAX_INITCODE_SIZE) - return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED); + return make_error_code(INITCODE_SIZE_EXCEEDED); // Compute and check if sender has enough balance for the theoretical maximum transaction cost. // Note this is different from tx_max_cost computed with effective gas price later. @@ -541,7 +541,7 @@ std::variant validate_transaction( max_total_fee += total_blob_gas * tx.max_blob_gas_price; } if (sender_acc.balance < max_total_fee) - return make_error_code(INSUFFICIENT_FUNDS); + return make_error_code(INSUFFICIENT_ACCOUNT_FUNDS); const auto [intrinsic_cost, min_cost] = compute_tx_intrinsic_cost(rev, tx); if (tx.gas_limit < std::max(intrinsic_cost, min_cost)) diff --git a/test/unittests/state_transition_tx_test.cpp b/test/unittests/state_transition_tx_test.cpp index b32eb1b9e5..b0d9d6afe2 100644 --- a/test/unittests/state_transition_tx_test.cpp +++ b/test/unittests/state_transition_tx_test.cpp @@ -46,7 +46,7 @@ TEST_F(state_transition, invalid_tx_non_existing_sender) tx.nonce = 0; pre.erase(Sender); - expect.tx_error = INSUFFICIENT_FUNDS; + expect.tx_error = INSUFFICIENT_ACCOUNT_FUNDS; expect.post[Sender].exists = false; } diff --git a/test/unittests/state_tx_test.cpp b/test/unittests/state_tx_test.cpp index 64d8f5839a..1db2b90934 100644 --- a/test/unittests/state_tx_test.cpp +++ b/test/unittests/state_tx_test.cpp @@ -32,13 +32,13 @@ TEST(state_tx, validate_nonce) EXPECT_EQ(std::get( validate_transaction(state, block, tx, EVMC_BERLIN, block.gas_limit, 0)) .message(), - "nonce too low"); + "TransactionException.NONCE_MISMATCH_TOO_LOW"); tx.nonce = 2; EXPECT_EQ(std::get( validate_transaction(state, block, tx, EVMC_BERLIN, block.gas_limit, 0)) .message(), - "nonce too high"); + "TransactionException.NONCE_MISMATCH_TOO_HIGH"); } TEST(state_tx, validate_sender) @@ -61,14 +61,14 @@ TEST(state_tx, validate_sender) EXPECT_EQ(std::get( validate_transaction(state, block, tx, EVMC_LONDON, block.gas_limit, 0)) .message(), - "max fee per gas less than block base fee"); + "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"); tx.max_gas_price = block.base_fee; EXPECT_EQ(std::get( validate_transaction(state, block, tx, EVMC_LONDON, block.gas_limit, 0)) .message(), - "insufficient funds for gas * price + value"); + "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS"); } TEST(state_tx, validate_blob_tx) @@ -93,7 +93,7 @@ TEST(state_tx, validate_blob_tx) static_cast(max_blob_gas_per_block(get_blob_params(EVMC_CANCUN))); EXPECT_EQ(std::get(validate_transaction( state, block, tx, EVMC_SHANGHAI, block.gas_limit, blob_gas_limit)), - make_error_code(ErrorCode::TX_TYPE_NOT_SUPPORTED)); + make_error_code(ErrorCode::TYPE_NOT_SUPPORTED)); EXPECT_EQ(std::get(validate_transaction(state, block, tx, EVMC_CANCUN, block.gas_limit, blob_gas_limit)) @@ -117,8 +117,8 @@ TEST(state_tx, validate_blob_tx) validate_transaction(state, block, tx, EVMC_CANCUN, block.gas_limit, g)); }; - EXPECT_EQ( - expect_error(blob_gas_limit), make_error_code(ErrorCode::BLOB_FEE_CAP_LESS_THAN_BLOCKS)); + EXPECT_EQ(expect_error(blob_gas_limit), + make_error_code(ErrorCode::INSUFFICIENT_MAX_FEE_PER_BLOB_GAS)); tx.max_blob_gas_price = 1; tx.blob_hashes.push_back( @@ -252,5 +252,5 @@ TEST(state_tx, max_gas_limit_exceeded) EXPECT_EQ(std::get( validate_transaction(state, block, tx, EVMC_OSAKA, block.gas_limit, 0)), - make_error_code(ErrorCode::MAX_GAS_LIMIT_EXCEEDED)); + make_error_code(ErrorCode::GAS_LIMIT_EXCEEDS_MAXIMUM)); } diff --git a/test/utils/statetest.hpp b/test/utils/statetest.hpp index 3f554daf5d..72639e73fc 100644 --- a/test/utils/statetest.hpp +++ b/test/utils/statetest.hpp @@ -145,9 +145,6 @@ json::json to_state_test(std::string_view test_name, const state::BlockInfo& blo state::Transaction& tx, const TestState& pre, evmc_revision rev, const std::variant& res, const TestState& post); -/// Returns the standardized error message for the transaction validation error. -[[nodiscard]] std::string get_invalid_tx_message(state::ErrorCode errc) noexcept; - std::vector load_state_tests(std::istream& input); diff --git a/test/utils/statetest_export.cpp b/test/utils/statetest_export.cpp index 11c3cf1742..2ab10415c3 100644 --- a/test/utils/statetest_export.cpp +++ b/test/utils/statetest_export.cpp @@ -24,60 +24,6 @@ std::string_view to_test_fork_name(evmc_revision rev) noexcept } } // namespace -[[nodiscard]] std::string get_invalid_tx_message(state::ErrorCode errc) noexcept -{ - using namespace state; - switch (errc) - { - case SUCCESS: - return ""; - case INTRINSIC_GAS_TOO_LOW: - return "TR_IntrinsicGas"; - case TX_TYPE_NOT_SUPPORTED: - return "TR_TypeNotSupported"; - case INSUFFICIENT_FUNDS: - return "TR_NoFunds"; - case NONCE_HAS_MAX_VALUE: - return "TR_NonceHasMaxValue:"; - case NONCE_TOO_HIGH: - return "TR_NonceTooHigh"; - case NONCE_TOO_LOW: - return "TR_NonceTooLow"; - case TIP_GT_FEE_CAP: - return "TR_TipGtFeeCap"; - case FEE_CAP_LESS_THAN_BLOCKS: - return "TR_FeeCapLessThanBlocks"; - case GAS_LIMIT_REACHED: - return "TR_GasLimitReached"; - case SENDER_NOT_EOA: - return "SenderNotEOA"; - case INIT_CODE_SIZE_LIMIT_EXCEEDED: - return "TR_InitCodeLimitExceeded"; - case CREATE_BLOB_TX: - return "TR_BLOBCREATE"; - case EMPTY_BLOB_HASHES_LIST: - return "TR_EMPTYBLOB"; - case INVALID_BLOB_HASH_VERSION: - return "TR_BLOBVERSION_INVALID"; - case BLOB_GAS_LIMIT_EXCEEDED: - return "TR_BLOBLIST_OVERSIZE"; - case INVALID_CHAIN_ID: - return "TransactionException.INVALID_CHAINID"; - case INVALID_ENCODING: - // EEST names every way an encoding can be malformed separately (RLP_*), so there is no - // single constant to export here; the plain message follows UNKNOWN_ERROR below. - return "Invalid transaction encoding"; - case INVALID_SIGNATURE: - return "TransactionException.INVALID_SIGNATURE_VRS"; - case UNKNOWN_ERROR: - return "Unknown error"; - default: - assert(false); - return "Wrong error code"; - } -} - - json::json to_json(const TestState& state) { json::json j = json::json::object(); @@ -195,8 +141,8 @@ json::json to_state_test(std::string_view test_name, const state::BlockInfo& blo if (holds_alternative(res)) { - jpost["expectException"] = get_invalid_tx_message( - static_cast(std::get(res).value())); + // The error message is the execution-spec-tests exception name. + jpost["expectException"] = std::get(res).message(); jpost["logs"] = hex0x(logs_hash(std::vector())); } else