Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP-3860: Limit and meter initcode #545

Merged
merged 3 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions lib/evmone/instructions_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,13 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept
const auto init_code_offset = static_cast<size_t>(init_code_offset_u256);
const auto init_code_size = static_cast<size_t>(init_code_size_u256);

if constexpr (Op == OP_CREATE2)
{
const auto salt_cost = num_words(init_code_size) * 6;
if ((state.gas_left -= salt_cost) < 0)
return EVMC_OUT_OF_GAS;
}
if (state.rev >= EVMC_SHANGHAI && init_code_size > 0xC000)
return EVMC_OUT_OF_GAS;

const auto init_code_word_cost = 6 * (Op == OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI);
const auto init_code_cost = num_words(init_code_size) * init_code_word_cost;
if ((state.gas_left -= init_code_cost) < 0)
return EVMC_OUT_OF_GAS;

if (state.msg->depth >= 1024)
return EVMC_SUCCESS; // "Light" failure.
Expand Down
2 changes: 1 addition & 1 deletion test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions test/state/host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 14 additions & 2 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace evmone::state
{
namespace
{
inline constexpr int64_t num_words(size_t size_in_bytes) noexcept
{
return static_cast<int64_t>((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;
Expand All @@ -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).
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
58 changes: 58 additions & 0 deletions test/unittests/evm_eip3860_initcode_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 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;
for (const auto& c : {create().input(0, calldataload(0)) + ret_top(),
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(c, evmc::uint256be{s});
if (rev >= EVMC_SHANGHAI && s > initcode_size_limit)
{
EXPECT_STATUS(EVMC_OUT_OF_GAS);
}
else
{
EXPECT_OUTPUT_INT(2);
}
}
}
}
}

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);
}