diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 52447856ca..6b68b94998 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace evmone @@ -225,14 +226,84 @@ std::variant validate_section_headers(byt std::accumulate(section_headers.container_sizes.begin(), section_headers.container_sizes.end(), uint64_t{0}); const auto remaining_container_size = static_cast(container_end - it); - // Only data section may be truncated, so remaining_container size must be at least - // declared_size_without_data + // Only data section may be truncated, so remaining_container size must be in + // [declared_size_without_data, declared_size_without_data + declared_data_size] if (remaining_container_size < section_bodies_without_data) return EOFValidationError::invalid_section_bodies_size; + if (remaining_container_size > section_bodies_without_data + section_headers.data_size) + return EOFValidationError::invalid_section_bodies_size; return section_headers; } +std::variant validate_header( + evmc_revision rev, bytes_view container) noexcept +{ + if (!is_eof_container(container)) + return EOFValidationError::invalid_prefix; + + const auto version = get_eof_version(container); + if (version != 1) + return EOFValidationError::eof_version_unknown; + + if (rev < EVMC_OSAKA) + return EOFValidationError::eof_version_unknown; + + // `offset` variable handled below is known to not be greater than the container size, as + // checked in `validate_section_headers`. Combined with the requirement for the container + // size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called), + // this allows us to cast `offset` to narrower integers. + assert(container.size() <= MAX_INITCODE_SIZE); + + auto section_headers_or_error = validate_section_headers(container); + if (const auto* error = std::get_if(§ion_headers_or_error)) + return *error; + + auto& section_headers = std::get(section_headers_or_error); + + const auto header_size = eof_header_size(section_headers); + + const auto type_section_offset = header_size; + + if (section_headers.type_size != + section_headers.code_sizes.size() * EOF1Header::TYPE_ENTRY_SIZE) + return EOFValidationError::invalid_type_section_size; + + auto offset = header_size + section_headers.type_size; + + std::vector code_offsets; + code_offsets.reserve(section_headers.code_sizes.size()); + for (const auto code_size : section_headers.code_sizes) + { + assert(offset <= std::numeric_limits::max()); + code_offsets.emplace_back(static_cast(offset)); + offset += code_size; + } + + std::vector container_offsets; + container_offsets.reserve(section_headers.container_sizes.size()); + for (const auto container_size : section_headers.container_sizes) + { + assert(offset <= std::numeric_limits::max()); + container_offsets.emplace_back(static_cast(offset)); + offset += container_size; + } + + assert(offset <= std::numeric_limits::max()); + const auto data_offset = static_cast(offset); + + return EOF1Header{ + .version = container[2], + .type_section_offset = type_section_offset, + .code_sizes = std::move(section_headers.code_sizes), + .code_offsets = std::move(code_offsets), + .data_size = section_headers.data_size, + .data_offset = data_offset, + .container_sizes = std::move(section_headers.container_sizes), + .container_offsets = std::move(container_offsets), + }; +} + EOFValidationError validate_types(bytes_view container, const EOF1Header& header) noexcept { for (size_t i = 0; i < header.get_type_count(); ++i) @@ -634,9 +705,6 @@ EOFValidationError validate_eof1( auto& header = std::get(error_or_header); - if (container.size() > static_cast(header.data_offset) + header.data_size) - return EOFValidationError::invalid_section_bodies_size; - if (const auto err = validate_types(container, header); err != EOFValidationError::success) return err; @@ -756,74 +824,6 @@ bool is_eof_container(bytes_view container) noexcept return container.starts_with(EOF_MAGIC); } -std::variant validate_header( - evmc_revision rev, bytes_view container) noexcept -{ - if (!is_eof_container(container)) - return EOFValidationError::invalid_prefix; - - const auto version = get_eof_version(container); - if (version != 1) - return EOFValidationError::eof_version_unknown; - - if (rev < EVMC_OSAKA) - return EOFValidationError::eof_version_unknown; - - // `offset` variable handled below is known to not be greater than the container size, as - // checked in `validate_section_headers`. Combined with the requirement for the container - // size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called), - // this allows us to cast `offset` to narrower integers. - assert(container.size() <= MAX_INITCODE_SIZE); - - auto section_headers_or_error = validate_section_headers(container); - if (const auto* error = std::get_if(§ion_headers_or_error)) - return *error; - - auto& section_headers = std::get(section_headers_or_error); - - const auto header_size = eof_header_size(section_headers); - - const auto type_section_offset = header_size; - - if (section_headers.type_size != - section_headers.code_sizes.size() * EOF1Header::TYPE_ENTRY_SIZE) - return EOFValidationError::invalid_type_section_size; - - auto offset = header_size + section_headers.type_size; - - std::vector code_offsets; - code_offsets.reserve(section_headers.code_sizes.size()); - for (const auto code_size : section_headers.code_sizes) - { - assert(offset <= std::numeric_limits::max()); - code_offsets.emplace_back(static_cast(offset)); - offset += code_size; - } - - std::vector container_offsets; - container_offsets.reserve(section_headers.container_sizes.size()); - for (const auto container_size : section_headers.container_sizes) - { - assert(offset <= std::numeric_limits::max()); - container_offsets.emplace_back(static_cast(offset)); - offset += container_size; - } - - assert(offset <= std::numeric_limits::max()); - const auto data_offset = static_cast(offset); - - return EOF1Header{ - .version = container[2], - .type_section_offset = type_section_offset, - .code_sizes = std::move(section_headers.code_sizes), - .code_offsets = std::move(code_offsets), - .data_size = section_headers.data_size, - .data_offset = data_offset, - .container_sizes = std::move(section_headers.container_sizes), - .container_offsets = std::move(container_offsets), - }; -} - /// This function expects the prefix and version to be valid, as it ignores it. EOF1Header read_valid_eof1_header(bytes_view container) { diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index 9aa7400c2e..323f0e6e2c 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include namespace evmone @@ -203,7 +202,7 @@ enum class EOFValidationError enum class ContainerKind : uint8_t { - /// Container that uses RETURNCODE. Can be used by EOFCREATE/TXCREATE/Creation transaction. + /// Container that uses RETURNCODE. Can be used by EOFCREATE/TXCREATE. initcode, /// Container that uses STOP/RETURN. Can be returned by RETURNCODE. runtime, @@ -213,10 +212,6 @@ enum class ContainerKind : uint8_t /// If the prefix is missing or invalid, 0 is returned meaning legacy code. [[nodiscard]] uint8_t get_eof_version(bytes_view container) noexcept; -/// Validates the header and returns its representation if successful. -[[nodiscard]] EVMC_EXPORT std::variant validate_header( - evmc_revision rev, bytes_view container) noexcept; - /// Validates whether given container is a valid EOF according to the rules of given revision. [[nodiscard]] EVMC_EXPORT EOFValidationError validate_eof( evmc_revision rev, ContainerKind kind, bytes_view container) noexcept; diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index 5edf4fd7d9..20f68eb45a 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -6,6 +6,8 @@ #include "eof.hpp" #include "instructions.hpp" +#include + constexpr int64_t MIN_RETAINED_GAS = 5000; constexpr int64_t MIN_CALLEE_GAS = 2300; constexpr int64_t CALL_VALUE_COST = 9000; diff --git a/test/state/errors.hpp b/test/state/errors.hpp index 68983bc300..4de6b4717e 100644 --- a/test/state/errors.hpp +++ b/test/state/errors.hpp @@ -28,6 +28,7 @@ enum ErrorCode : int EMPTY_BLOB_HASHES_LIST, INVALID_BLOB_HASH_VERSION, BLOB_GAS_LIMIT_EXCEEDED, + EOF_CREATION_TRANSACTION, CREATE_SET_CODE_TX, EMPTY_AUTHORIZATION_LIST, INIT_CODE_EMPTY, @@ -81,6 +82,8 @@ inline const std::error_category& evmone_category() noexcept return "invalid blob hash version"; case BLOB_GAS_LIMIT_EXCEEDED: return "blob gas limit exceeded"; + case EOF_CREATION_TRANSACTION: + return "EOF initcode in creation transaction"; case CREATE_SET_CODE_TX: return "set code transaction must not be a create transaction"; case EMPTY_AUTHORIZATION_LIST: diff --git a/test/state/host.cpp b/test/state/host.cpp index 8be52c43c1..f9af5f5654 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -277,42 +277,9 @@ std::optional Host::prepare_message(evmc_message msg) noexcept } else { - assert(msg.kind == EVMC_EOFCREATE); - const bytes_view input = {msg.input_data, msg.input_size}; - - // Indicator of an EOF creation tx - EVMC_EOFCREATE at depth 0. - if (msg.depth == 0) - { - // Assert this is a legacy EOF creation tx. - assert(is_eof_container(input)); - const auto header_or_error = validate_header(m_rev, input); - - const auto* header = std::get_if(&header_or_error); - if (header == nullptr) - return {}; // Light early exception. - - if (!header->has_full_data(msg.input_size)) - return {}; // Light early exception. - - const auto container_size = - static_cast(header->data_offset) + header->data_size; - // Follows from the header->can_init condition above. - assert(container_size <= msg.input_size); - - msg.code = msg.input_data; - msg.code_size = container_size; - msg.input_data = msg.input_data + container_size; - msg.input_size = msg.input_size - container_size; - - if (validate_eof(m_rev, ContainerKind::initcode, {msg.code, msg.code_size}) != - EOFValidationError::success) - return {}; // Light early exception. - - msg.recipient = compute_create_address(msg.sender, creation_sender_nonce); - } // EOFCREATE or TXCREATE - else - msg.recipient = compute_eofcreate_address(msg.sender, msg.create2_salt); + assert(msg.kind == EVMC_EOFCREATE); + msg.recipient = compute_eofcreate_address(msg.sender, msg.create2_salt); } // By EIP-2929, the access to new created address is never reverted. @@ -395,7 +362,7 @@ evmc::Result Host::create(const evmc_message& msg) noexcept { if (m_rev >= EVMC_OSAKA) { - // Only EOFCREATE/TXCREATE/EOF-creation-tx is allowed to deploy code starting with + // Only EOFCREATE/TXCREATE is allowed to deploy code starting with // EF. It must be valid EOF, which was validated before execution. if (msg.kind != EVMC_EOFCREATE) return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; diff --git a/test/state/state.cpp b/test/state/state.cpp index 309a86fce8..b615218c6d 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -75,7 +75,7 @@ TransactionCost compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& static constexpr auto INITCODE_WORD_COST = 2; static constexpr auto TOTAL_COST_FLOOR_PER_TOKEN = 10; - const auto is_create = !tx.to.has_value(); // Covers also EOF creation transactions. + const auto is_create = !tx.to.has_value(); const auto create_cost = (is_create && rev >= EVMC_HOMESTEAD) ? TX_CREATE_COST : 0; @@ -186,17 +186,12 @@ int64_t process_authorization_list( return delegation_refund; } -evmc_message build_message( - const Transaction& tx, int64_t execution_gas_limit, evmc_revision rev) noexcept +evmc_message build_message(const Transaction& tx, int64_t execution_gas_limit) noexcept { const auto recipient = tx.to.has_value() ? *tx.to : evmc::address{}; - const auto is_legacy_eof_create = - rev >= EVMC_OSAKA && !tx.to.has_value() && is_eof_container(tx.data); - - return {.kind = is_legacy_eof_create ? EVMC_EOFCREATE : - tx.to.has_value() ? EVMC_CALL : // NOLINT(readability-avoid-nested-conditional-operator) - EVMC_CREATE, + return { + .kind = tx.to.has_value() ? EVMC_CALL : EVMC_CREATE, .flags = 0, .depth = 0, .gas = execution_gas_limit, @@ -208,7 +203,8 @@ evmc_message build_message( .create2_salt = {}, .code_address = recipient, .code = nullptr, - .code_size = 0}; + .code_size = 0, + }; } } // namespace @@ -531,6 +527,9 @@ std::variant validate_transaction( if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > MAX_INITCODE_SIZE) return make_error_code(INIT_CODE_SIZE_LIMIT_EXCEEDED); + if (rev >= EVMC_OSAKA && !tx.to.has_value() && is_eof_container(tx.data)) + return make_error_code(EOF_CREATION_TRANSACTION); + // 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. // The computation cannot overflow if done with 512-bit precision. @@ -634,7 +633,7 @@ TransactionReceipt transition(const StateView& state_view, const BlockInfo& bloc if (rev >= EVMC_SHANGHAI) host.access_account(block.coinbase); - auto message = build_message(tx, tx_props.execution_gas_limit, rev); + auto message = build_message(tx, tx_props.execution_gas_limit); if (tx.to.has_value()) { if (const auto delegate = get_delegate_address(host, *tx.to)) diff --git a/test/statetest/statetest_export.cpp b/test/statetest/statetest_export.cpp index 37daea22ea..d0e6f7cc47 100644 --- a/test/statetest/statetest_export.cpp +++ b/test/statetest/statetest_export.cpp @@ -49,6 +49,8 @@ namespace evmone::test return "TR_BLOBVERSION_INVALID"; case BLOB_GAS_LIMIT_EXCEEDED: return "TR_BLOBLIST_OVERSIZE"; + case EOF_CREATION_TRANSACTION: + return "TR_EOFCreationTransaction"; case UNKNOWN_ERROR: return "Unknown error"; default: diff --git a/test/unittests/eof_example_test.cpp b/test/unittests/eof_example_test.cpp index e6d0af942f..1e2ff1fd29 100644 --- a/test/unittests/eof_example_test.cpp +++ b/test/unittests/eof_example_test.cpp @@ -119,46 +119,6 @@ TEST_F(state_transition, eof_examples_callf) expect.post[*tx.to].exists = true; } -TEST_F(state_transition, eof_examples_creation_tx) -{ - // # Example 4 - // - // A creation transaction used to create a new EOF contract. - - rev = EVMC_OSAKA; - - const auto initcontainer = bytecode( - ////////////////// - // Initcontainer - // Code section: PUSH0 [aux data size], PUSH0 [aux data offset] and - // RETURNCODE first subcontainer - // | - // Header: 1 code section 4 bytes long | - // | | - // version | Header terminator - // | |___________ | |________ - "EF00 01 01 0004 02 0001 0004 03 0001 00000014 FF 0000 00 00 80 0002 5F5F EE00" - // |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾ - // | | Header: data section 0 bytes long - // | | | - // Header: types section 4 bytes long Types section: first code section - // | 0 inputs, non-returning, - // | max stack height 2 - // Header: 1 subcontainer 20 bytes long - // - ////////////////// - // Deployed container (contract doing nothing, see Example 1) - "EF00 01 01 0004 02 0001 0001 FF 0000 00 00 80 0000 00"); - - // Put the initcontainer in the `data` field of the transaction, appending some calldata. - tx.data = initcontainer + "ABCDEF"; - // Empty `to` field. - tx.to = std::nullopt; - - // Address of the newly created contract is calculated using the deployer's address and nonce. - expect.post[0x3442a1dec1e72f337007125aa67221498cdd759d_address].exists = true; -} - TEST_F(state_transition, eof_examples_eofcreate) { // # Example 5 diff --git a/test/unittests/state_transition_eof_create_test.cpp b/test/unittests/state_transition_eof_create_test.cpp index e3b5481fbf..257f8f5306 100644 --- a/test/unittests/state_transition_eof_create_test.cpp +++ b/test/unittests/state_transition_eof_create_test.cpp @@ -13,6 +13,18 @@ namespace constexpr bytes32 Salt{0xff}; } + +TEST_F(state_transition, create_tx_with_eof_initcode) +{ + rev = EVMC_OSAKA; + + const bytecode init_container = eof_bytecode(ret(0, 1)); + + tx.data = init_container; + + expect.tx_error = EOF_CREATION_TRANSACTION; +} + TEST_F(state_transition, create_with_eof_initcode) { rev = EVMC_OSAKA; @@ -837,351 +849,3 @@ TEST_F(state_transition, eofcreate_call_created_contract) expect.post[*tx.to].storage[0x04_bytes32] = 0x01_bytes32; expect.post[create_address].nonce = 1; } - -TEST_F(state_transition, creation_tx) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = deploy_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_deploy_data) -{ - rev = EVMC_OSAKA; - const auto deploy_data = "abcdef"_hex; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); - - const auto init_code = returncode(0, 0, 0); - const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = deploy_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_static_auxdata_in_calldata) -{ - rev = EVMC_OSAKA; - const auto deploy_data = "abcdef"_hex; - // aux_data will be appended as calldata to the creation tx input, and later appended to the - // deployed contract's data section on RETURNCODE. - const auto aux_data = "aabbccddeeff"_hex; - const auto deploy_data_size = static_cast(deploy_data.size()); - const auto aux_data_size = static_cast(aux_data.size()); - - // aux_data_size included in the declared data section size - static data. - const auto deploy_container = - eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size + aux_data_size); - - const auto init_code = calldatacopy(0, 0, OP_CALLDATASIZE) + returncode(0, 0, OP_CALLDATASIZE); - const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); - - tx.data = init_container + bytecode(aux_data); - const auto expected_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data); - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = expected_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_dynamic_auxdata_in_calldata) -{ - rev = EVMC_OSAKA; - const auto deploy_data = "abcdef"_hex; - // aux_data will be appended as calldata to the creation tx input, and later appended the - // deployed contract's data section on RETURNCODE. - const auto aux_data = "aabbccddeeff"_hex; - const auto deploy_data_size = static_cast(deploy_data.size()); - - // aux_data_size not included in the declared data section size - dynamic data. - const auto deploy_container = - eof_bytecode(bytecode(OP_INVALID)).data(deploy_data, deploy_data_size); - - const auto init_code = calldatacopy(0, 0, OP_CALLDATASIZE) + returncode(0, 0, OP_CALLDATASIZE); - const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); - - tx.data = init_container + bytecode(aux_data); - const auto expected_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data + aux_data); - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = expected_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_dataloadn_referring_to_auxdata) -{ - rev = EVMC_OSAKA; - const auto deploy_data = bytes(64, 0x01); - const auto aux_data = bytes(32, 0x03); - const auto deploy_data_size = static_cast(deploy_data.size() + aux_data.size()); - // DATALOADN{64} - referring to data that will be appended as aux_data - const auto deploy_code = bytecode(OP_DATALOADN) + "0040" + ret_top(); - const auto deploy_container = eof_bytecode(deploy_code, 2).data(deploy_data, deploy_data_size); - - const auto init_code = calldatacopy(0, 0, OP_CALLDATASIZE) + returncode(0, 0, OP_CALLDATASIZE); - const bytecode init_container = eof_bytecode(init_code, 3).container(deploy_container); - - tx.data = init_container + bytecode(aux_data); - - const auto expected_container = eof_bytecode(deploy_code, 2).data(deploy_data + aux_data); - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = expected_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_initcontainer_aborts) -{ - rev = EVMC_OSAKA; - const auto init_code = bytecode{Opcode{OP_INVALID}}; - const bytecode init_container = eof_bytecode(init_code, 0); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_INVALID_INSTRUCTION; -} - -TEST_F(state_transition, creation_tx_initcontainer_return) -{ - rev = EVMC_OSAKA; - const auto init_code = bytecode{0xaa + ret_top()}; - const bytecode init_container = eof_bytecode(init_code, 2); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; -} - -TEST_F(state_transition, creation_tx_initcontainer_stop) -{ - rev = EVMC_OSAKA; - const auto init_code = bytecode{Opcode{OP_STOP}}; - const bytecode init_container = eof_bytecode(init_code, 0); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; -} - -TEST_F(state_transition, creation_tx_initcontainer_max_size) -{ - rev = EVMC_OSAKA; - block.gas_limit = 10'000'000; - tx.gas_limit = block.gas_limit; - pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; - - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - const bytecode init_container_no_data = eof_bytecode(init_code, 2).container(deploy_container); - const auto data_size = 0xc000 - init_container_no_data.size(); - const bytecode init_container = - eof_bytecode(init_code, 2).container(deploy_container).data(bytes(data_size, 0)); - EXPECT_EQ(init_container.size(), 0xc000); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = deploy_container; - expect.post[create_address].nonce = 1; -} - -TEST_F(state_transition, creation_tx_initcontainer_too_large) -{ - rev = EVMC_OSAKA; - block.gas_limit = 10'000'000; - tx.gas_limit = block.gas_limit; - pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; - - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - const bytecode init_container_no_data = eof_bytecode(init_code, 2).container(deploy_container); - const auto data_size = 0xc001 - init_container_no_data.size(); - const bytecode init_container = - eof_bytecode(init_code, 2).container(deploy_container).data(bytes(data_size, 0)); - EXPECT_EQ(init_container.size(), 0xc001); - - tx.data = init_container; - - expect.tx_error = INIT_CODE_SIZE_LIMIT_EXCEEDED; -} - -TEST_F(state_transition, creation_tx_deploy_container_max_size) -{ - rev = EVMC_OSAKA; - block.gas_limit = 10'000'000; - tx.gas_limit = block.gas_limit; - pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; - - const auto eof_header_size = - static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); - const auto deploy_code = (0x5fff - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; - const bytecode deploy_container = eof_bytecode(deploy_code); - EXPECT_EQ(deploy_container.size(), 0x6000); - - // no aux data - const auto init_code = returncode(0, 0, 0); - const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = deploy_container; -} - -TEST_F(state_transition, creation_tx_deploy_container_too_large) -{ - rev = EVMC_OSAKA; - block.gas_limit = 10'000'000; - tx.gas_limit = block.gas_limit; - pre.get(tx.sender).balance = tx.gas_limit * tx.max_gas_price + tx.value + 1; - - const auto eof_header_size = - static_cast(bytecode{eof_bytecode(Opcode{OP_INVALID})}.size() - 1); - const auto deploy_code = (0x6000 - eof_header_size) * bytecode{Opcode{OP_JUMPDEST}} + OP_STOP; - const bytecode deploy_container = eof_bytecode(deploy_code); - EXPECT_EQ(deploy_container.size(), 0x6001); - - // no aux data - const auto init_code = returncode(0, 0, 0); - const bytecode init_container = eof_bytecode(init_code, 2).container(deploy_container); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; -} - -TEST_F(state_transition, creation_tx_nested_eofcreate) -{ - rev = EVMC_OSAKA; - const auto deploy_data = "abcdef"_hex; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)).data(deploy_data); - - const auto deploy_data_nested = "ffffff"_hex; - const auto deploy_container_nested = - eof_bytecode(bytecode(OP_INVALID)).data(deploy_data_nested); - - const auto init_code_nested = returncode(0, 0, 0); - const bytecode init_container_nested = - eof_bytecode(init_code_nested, 2).container(deploy_container_nested); - - const auto init_code = sstore(0, eofcreate().container(1).salt(Salt)) + returncode(0, 0, 0); - const bytecode init_container = - eof_bytecode(init_code, 4).container(deploy_container).container(init_container_nested); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - const auto create_address = compute_create_address(Sender, pre.get(Sender).nonce); - expect.post[create_address].code = deploy_container; - expect.post[create_address].nonce = 2; - const auto create_address_nested = compute_eofcreate_address(create_address, Salt); - expect.post[create_address].storage[0x00_bytes32] = to_bytes32(create_address_nested); - expect.post[create_address_nested].code = deploy_container_nested; - expect.post[create_address_nested].nonce = 1; -} - -TEST_F(state_transition, creation_tx_invalid_initcode_header) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); - - assert(init_container[3] == 0x01); - init_container[3] = 0x04; // Data section as first section in the header invalid. - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; - expect.gas_used = 53524; -} - -TEST_F(state_transition, creation_tx_invalid_initcode) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - const bytes init_container = - eof_bytecode(init_code, 123).container(deploy_container); // Invalid EOF - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; - expect.gas_used = 53524; -} - -TEST_F(state_transition, creation_tx_truncated_data_initcode) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - const bytes init_container = - eof_bytecode(init_code, 2).data("", 1).container(deploy_container); // Truncated data - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; - expect.gas_used = 53536; -} - -TEST_F(state_transition, creation_tx_invalid_deploycode) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID), 123); // Invalid EOF - - const auto init_code = returncode(0, 0, 0); - const bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; - expect.gas_used = 53536; -} - -TEST_F(state_transition, creation_tx_invalid_eof_version) -{ - rev = EVMC_OSAKA; - const auto deploy_container = eof_bytecode(bytecode(OP_INVALID)); - - const auto init_code = returncode(0, 0, 0); - bytes init_container = eof_bytecode(init_code, 2).container(deploy_container); - - assert(init_container[2] == 0x01); - init_container[2] = 0x02; - - tx.data = init_container; - - expect.post[Sender].nonce = pre.get(Sender).nonce + 1; - expect.status = EVMC_FAILURE; - expect.gas_used = 53524; -} diff --git a/test/unittests/state_tx_test.cpp b/test/unittests/state_tx_test.cpp index 7da49ddfc1..1c0c4e31c4 100644 --- a/test/unittests/state_tx_test.cpp +++ b/test/unittests/state_tx_test.cpp @@ -149,10 +149,14 @@ TEST(state_tx, validate_eof_create_transaction) }; const TestState state{{tx.sender, {.nonce = 1, .balance = 1'000'000}}}; - ASSERT_FALSE(holds_alternative( + EXPECT_FALSE(holds_alternative( validate_transaction(state, block, tx, EVMC_CANCUN, 60000, 0))); - ASSERT_FALSE(holds_alternative( + EXPECT_FALSE(holds_alternative( validate_transaction(state, block, tx, EVMC_PRAGUE, 60000, 0))); + EXPECT_EQ( + std::get(validate_transaction(state, block, tx, EVMC_OSAKA, 60000, 0)) + .message(), + "EOF initcode in creation transaction"); } TEST(state_tx, validate_tx_data_cost)