diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index 73d1ec2896..e9a6ba910a 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -137,12 +137,23 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept const auto init_code_offset = static_cast(init_code_offset_u256); const auto init_code_size = static_cast(init_code_size_u256); + if (state.rev >= EVMC_SHANGHAI && init_code_size > 0xC000) + return EVMC_SUCCESS; // "Light" failure. + + const auto init_code_words = num_words(init_code_size); if constexpr (Op == OP_CREATE2) { - const auto salt_cost = num_words(init_code_size) * 6; + const auto salt_cost = init_code_words * 6; if ((state.gas_left -= salt_cost) < 0) return EVMC_OUT_OF_GAS; } + if (state.rev >= EVMC_SHANGHAI) + { + // TODO: Merge two init code cost checks. + const auto init_code_cost = init_code_words * 2; + if ((state.gas_left -= init_code_cost) < 0) + return EVMC_OUT_OF_GAS; + } if (state.msg->depth >= 1024) return EVMC_SUCCESS; // "Light" failure. diff --git a/test/state/host.cpp b/test/state/host.cpp index 0387295865..8c4ae4c79d 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -207,7 +207,7 @@ evmc::Result Host::create(const evmc_message& msg) noexcept assert(gas_left >= 0); const bytes_view code{result.output_data, result.output_size}; - if (m_rev >= EVMC_SPURIOUS_DRAGON && code.size() > 0x6000) + if (m_rev >= EVMC_SPURIOUS_DRAGON && code.size() > max_code_size) return evmc::Result{EVMC_FAILURE}; // Code deployment cost. diff --git a/test/state/host.hpp b/test/state/host.hpp index 87d5250eae..021b05af8d 100644 --- a/test/state/host.hpp +++ b/test/state/host.hpp @@ -12,6 +12,9 @@ namespace evmone::state { using evmc::uint256be; +inline constexpr size_t max_code_size = 0x6000; +inline constexpr size_t max_initcode_size = 2 * max_code_size; + class Host : public evmc::Host { evmc_revision m_rev; diff --git a/test/state/state.cpp b/test/state/state.cpp index a161daa574..9b6e552372 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -11,6 +11,11 @@ namespace evmone::state { namespace { +inline constexpr int64_t num_words(size_t size_in_bytes) noexcept +{ + return static_cast((size_in_bytes + 31) / 32); +} + int64_t compute_tx_data_cost(evmc_revision rev, bytes_view data) noexcept { constexpr int64_t zero_byte_cost = 4; @@ -36,9 +41,13 @@ int64_t compute_tx_intrinsic_cost(evmc_revision rev, const Transaction& tx) noex { static constexpr auto call_tx_cost = 21000; static constexpr auto create_tx_cost = 53000; - const bool is_create = !tx.to.has_value(); + static constexpr auto initcode_word_cost = 2; + const auto is_create = !tx.to.has_value(); + const auto initcode_cost = + is_create && rev >= EVMC_SHANGHAI ? initcode_word_cost * num_words(tx.data.size()) : 0; const auto tx_cost = is_create && rev >= EVMC_HOMESTEAD ? create_tx_cost : call_tx_cost; - return tx_cost + compute_tx_data_cost(rev, tx.data) + compute_access_list_cost(tx.access_list); + return tx_cost + compute_tx_data_cost(rev, tx.data) + compute_access_list_cost(tx.access_list) + + initcode_cost; } /// Validates transaction and computes its execution gas limit (the amount of gas provided to EVM). @@ -68,6 +77,9 @@ int64_t validate_transaction(const Account& sender_acc, const BlockInfo& block, if (sender_acc.nonce == Account::NonceMax) return -1; + if (rev >= EVMC_SHANGHAI && !tx.to.has_value() && tx.data.size() > max_initcode_size) + return -1; // initcode size is limited by EIP-3860. + // 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. diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 33f6e44b84..e53d0f83e7 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(evmone-unittests evm_eip2929_test.cpp evm_eip3198_basefee_test.cpp evm_eip3855_push0_test.cpp + evm_eip3860_initcode_test.cpp evm_eof_test.cpp evm_memory_test.cpp evm_state_test.cpp diff --git a/test/unittests/evm_eip3860_initcode_test.cpp b/test/unittests/evm_eip3860_initcode_test.cpp new file mode 100644 index 0000000000..0e229f8158 --- /dev/null +++ b/test/unittests/evm_eip3860_initcode_test.cpp @@ -0,0 +1,88 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2019-2020 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains EVM unit tests for EIP-3860 "Limit and meter initcode" +/// https://eips.ethereum.org/EIPS/eip-3860 + +#include "evm_fixture.hpp" + +using namespace evmc::literals; +using evmone::test::evm; + +inline constexpr size_t initcode_size_limit = 0xc000; + +TEST_P(evm, create_initcode_limit) +{ + host.call_result.create_address = 0x02_address; + const auto code = create().input(0, calldataload(0)) + ret_top(); + for (const auto r : {EVMC_PARIS, EVMC_SHANGHAI}) + { + rev = r; + for (const auto s : {initcode_size_limit, initcode_size_limit + 1}) + { + execute(code, evmc::uint256be{s}); + EXPECT_STATUS(EVMC_SUCCESS); + const unsigned expected_output = + rev >= EVMC_SHANGHAI && s > initcode_size_limit ? 0 : 2; + EXPECT_OUTPUT_INT(expected_output); + } + } +} + +TEST_P(evm, create2_initcode_limit) +{ + host.call_result.create_address = 0x02_address; + const auto code = create2().input(0, calldataload(0)) + ret_top(); + for (const auto r : {EVMC_PARIS, EVMC_SHANGHAI}) + { + rev = r; + for (const auto s : {initcode_size_limit, initcode_size_limit + 1}) + { + execute(code, evmc::uint256be{s}); + EXPECT_STATUS(EVMC_SUCCESS); + const unsigned expected_output = + rev >= EVMC_SHANGHAI && s > initcode_size_limit ? 0 : 2; + EXPECT_OUTPUT_INT(expected_output); + } + } +} + +TEST_P(evm, create_initcode_gas_cost) +{ + rev = EVMC_SHANGHAI; + const auto code = create().input(0, calldataload(0)); + execute(44300, code, evmc::uint256be{initcode_size_limit}); + EXPECT_GAS_USED(EVMC_SUCCESS, 44300); + execute(44299, code, evmc::uint256be{initcode_size_limit}); + EXPECT_STATUS(EVMC_OUT_OF_GAS); +} + +TEST_P(evm, create2_initcode_gas_cost) +{ + rev = EVMC_SHANGHAI; + const auto code = create2().input(0, calldataload(0)); + execute(53519, code, evmc::uint256be{initcode_size_limit}); + EXPECT_GAS_USED(EVMC_SUCCESS, 53519); + execute(53518, code, evmc::uint256be{initcode_size_limit}); + EXPECT_STATUS(EVMC_OUT_OF_GAS); +} + +TEST_P(evm, create2_stack_check) +{ + // Checks if CREATE2 properly handles values on EVM stack. + rev = EVMC_SHANGHAI; + host.call_result.create_address = 0xca_address; + const auto code = + push(0x84) + create2().input(0, calldataload(0)).salt(0x42) + sstore(0) + sstore(1); + + for (const auto& input : {0xC000_bytes32, 0xC001_bytes32}) + { + execute(code, input); + EXPECT_STATUS(EVMC_SUCCESS); + auto& storage = host.accounts[msg.recipient].storage; + EXPECT_EQ( + storage[0x00_bytes32].current, input == 0xC001_bytes32 ? 0x00_bytes32 : 0xca_bytes32); + EXPECT_EQ(storage[0x01_bytes32].current, 0x84_bytes32); + } +} diff --git a/test/utils/bytecode.hpp b/test/utils/bytecode.hpp index 5cd9b3e53e..bf5c657c8d 100644 --- a/test/utils/bytecode.hpp +++ b/test/utils/bytecode.hpp @@ -282,6 +282,11 @@ inline bytecode sstore(bytecode index, bytecode value) return value + index + OP_SSTORE; } +inline bytecode sstore(bytecode index) +{ + return index + OP_SSTORE; +} + inline bytecode sload(bytecode index) { return index + OP_SLOAD;