From 3a2f2377f8494971cdea6cba187784fba96a8f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 7 Apr 2021 10:50:56 +0200 Subject: [PATCH 1/8] Update EVMC to 8.0.0-alpha.2 with EIP-2929 --- evmc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evmc b/evmc index 60bbdf02a1..56650cd567 160000 --- a/evmc +++ b/evmc @@ -1 +1 @@ -Subproject commit 60bbdf02a1cbb39ab48782621a709dd3933b9e28 +Subproject commit 56650cd56708f2ab8136f352c6b500fbd08b7ab1 From f001fff9aa8af2faa9512ec1b446b25f3a2c27d5 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Sat, 13 Feb 2021 16:27:53 +0100 Subject: [PATCH 2/8] Set instruction costs for Berlin / EIP-2929 --- lib/evmone/instruction_traits.hpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/evmone/instruction_traits.hpp b/lib/evmone/instruction_traits.hpp index fced3e7f2a..c7ba45eebf 100644 --- a/lib/evmone/instruction_traits.hpp +++ b/lib/evmone/instruction_traits.hpp @@ -1,12 +1,18 @@ // evmone: Fast Ethereum Virtual Machine implementation // Copyright 2020 The evmone Authors. // SPDX-License-Identifier: Apache-2.0 +#pragma once #include #include namespace evmone::instr { +/// EIP-2929 constants (https://eips.ethereum.org/EIPS/eip-2929). +inline constexpr auto cold_sload_cost = 2100; +inline constexpr auto cold_account_access_cost = 2600; +inline constexpr auto warm_storage_read_cost = 100; + /// The EVM instruction traits. struct Traits { @@ -333,5 +339,18 @@ constexpr inline std::array gas_costs = []() noexce }(); template <> -constexpr inline auto gas_costs = gas_costs; +constexpr inline std::array gas_costs = []() noexcept { + auto table = gas_costs; + table[OP_EXTCODESIZE] = warm_storage_read_cost; + table[OP_EXTCODECOPY] = warm_storage_read_cost; + table[OP_EXTCODEHASH] = warm_storage_read_cost; + table[OP_BALANCE] = warm_storage_read_cost; + table[OP_CALL] = warm_storage_read_cost; + table[OP_CALLCODE] = warm_storage_read_cost; + table[OP_DELEGATECALL] = warm_storage_read_cost; + table[OP_STATICCALL] = warm_storage_read_cost; + table[OP_SLOAD] = warm_storage_read_cost; + return table; +}(); + } // namespace evmone::instr From c163af5331347a4316336240e098e8046bf1a24a Mon Sep 17 00:00:00 2001 From: yperbasis Date: Fri, 12 Feb 2021 16:06:18 +0100 Subject: [PATCH 3/8] Implement EIP-2929 for SLOAD and SSTORE --- lib/evmone/baseline.cpp | 9 ++++++- lib/evmone/instructions.hpp | 50 ++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index d17396f6a4..e1b7150ea9 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -381,8 +381,15 @@ evmc_result baseline_execute(ExecutionState& state) noexcept msize(state); break; case OP_SLOAD: - sload(state); + { + const auto status_code = sload(state); + if (status_code != EVMC_SUCCESS) + { + state.status = status_code; + goto exit; + } break; + } case OP_SSTORE: { const auto status_code = sstore(state); diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index f09ebfb826..83d3be6c20 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -4,8 +4,8 @@ #pragma once #include "execution_state.hpp" +#include "instruction_traits.hpp" #include -#include namespace evmone { @@ -561,11 +561,25 @@ inline evmc_status_code mstore8(ExecutionState& state) noexcept return EVMC_SUCCESS; } -inline void sload(ExecutionState& state) noexcept +inline evmc_status_code sload(ExecutionState& state) noexcept { auto& x = state.stack.top(); - x = intx::be::load( - state.host.get_storage(state.msg->destination, intx::be::store(x))); + const auto key = intx::be::store(x); + + if (state.rev >= EVMC_BERLIN && + state.host.access_storage(state.msg->destination, key) == EVMC_ACCESS_COLD) + { + // The warm storage access cost is already applied (from the cost table). + // Here we need to apply additional cold storage access cost. + constexpr auto additional_cold_sload_cost = + instr::cold_sload_cost - instr::warm_storage_read_cost; + if ((state.gas_left -= additional_cold_sload_cost) < 0) + return EVMC_OUT_OF_GAS; + } + + x = intx::be::load(state.host.get_storage(state.msg->destination, key)); + + return EVMC_SUCCESS; } inline evmc_status_code sstore(ExecutionState& state) noexcept @@ -578,12 +592,21 @@ inline evmc_status_code sstore(ExecutionState& state) noexcept const auto key = intx::be::store(state.stack.pop()); const auto value = intx::be::store(state.stack.pop()); - const auto status = state.host.set_storage(state.msg->destination, key, value); + int cost = 0; + if (state.rev >= EVMC_BERLIN && + state.host.access_storage(state.msg->destination, key) == EVMC_ACCESS_COLD) + cost = instr::cold_sload_cost; + + const auto status = state.host.set_storage(state.msg->destination, key, value); + switch (status) { case EVMC_STORAGE_UNCHANGED: - if (state.rev >= EVMC_ISTANBUL) + case EVMC_STORAGE_MODIFIED_AGAIN: + if (state.rev >= EVMC_BERLIN) + cost += instr::warm_storage_read_cost; + else if (state.rev == EVMC_ISTANBUL) cost = 800; else if (state.rev == EVMC_CONSTANTINOPLE) cost = 200; @@ -591,21 +614,14 @@ inline evmc_status_code sstore(ExecutionState& state) noexcept cost = 5000; break; case EVMC_STORAGE_MODIFIED: - cost = 5000; - break; - case EVMC_STORAGE_MODIFIED_AGAIN: - if (state.rev >= EVMC_ISTANBUL) - cost = 800; - else if (state.rev == EVMC_CONSTANTINOPLE) - cost = 200; + case EVMC_STORAGE_DELETED: + if (state.rev >= EVMC_BERLIN) + cost += 5000 - instr::cold_sload_cost; else cost = 5000; break; case EVMC_STORAGE_ADDED: - cost = 20000; - break; - case EVMC_STORAGE_DELETED: - cost = 5000; + cost += 20000; break; } if ((state.gas_left -= cost) < 0) From 6c034d2ca38f605880cf396c99e4fc661499fce1 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Sun, 14 Feb 2021 16:39:35 +0100 Subject: [PATCH 4/8] Implement EIP-2929 for EXT* & BALANCE instructions --- lib/evmone/baseline.cpp | 28 ++++++++++++++++--- lib/evmone/instruction_traits.hpp | 9 +++++++ lib/evmone/instructions.hpp | 45 ++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index e1b7150ea9..db8a72cdbb 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -226,8 +226,15 @@ evmc_result baseline_execute(ExecutionState& state) noexcept address(state); break; case OP_BALANCE: - balance(state); + { + const auto status_code = balance(state); + if (status_code != EVMC_SUCCESS) + { + state.status = status_code; + goto exit; + } break; + } case OP_ORIGIN: origin(state); break; @@ -270,8 +277,15 @@ evmc_result baseline_execute(ExecutionState& state) noexcept gasprice(state); break; case OP_EXTCODESIZE: - extcodesize(state); + { + const auto status_code = extcodesize(state); + if (status_code != EVMC_SUCCESS) + { + state.status = status_code; + goto exit; + } break; + } case OP_EXTCODECOPY: { const auto status_code = extcodecopy(state); @@ -296,9 +310,15 @@ evmc_result baseline_execute(ExecutionState& state) noexcept break; } case OP_EXTCODEHASH: - extcodehash(state); + { + const auto status_code = extcodehash(state); + if (status_code != EVMC_SUCCESS) + { + state.status = status_code; + goto exit; + } break; - + } case OP_BLOCKHASH: blockhash(state); break; diff --git a/lib/evmone/instruction_traits.hpp b/lib/evmone/instruction_traits.hpp index c7ba45eebf..ba79bc6144 100644 --- a/lib/evmone/instruction_traits.hpp +++ b/lib/evmone/instruction_traits.hpp @@ -9,10 +9,19 @@ namespace evmone::instr { /// EIP-2929 constants (https://eips.ethereum.org/EIPS/eip-2929). +/// @{ inline constexpr auto cold_sload_cost = 2100; inline constexpr auto cold_account_access_cost = 2600; inline constexpr auto warm_storage_read_cost = 100; +/// Additional cold account access cost. +/// +/// The warm access cost is unconditionally applied for every account access instruction. +/// If the access turns out to be cold, this cost must be applied additionally. +inline constexpr auto additional_cold_account_access_cost = + cold_account_access_cost - warm_storage_read_cost; +/// @} + /// The EVM instruction traits. struct Traits { diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 83d3be6c20..4e5ad96a5f 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -279,10 +279,19 @@ inline void address(ExecutionState& state) noexcept state.stack.push(intx::be::load(state.msg->destination)); } -inline void balance(ExecutionState& state) noexcept +inline evmc_status_code balance(ExecutionState& state) noexcept { auto& x = state.stack.top(); - x = intx::be::load(state.host.get_balance(intx::be::trunc(x))); + const auto addr = intx::be::trunc(x); + + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + + x = intx::be::load(state.host.get_balance(addr)); + return EVMC_SUCCESS; } inline void origin(ExecutionState& state) noexcept @@ -394,10 +403,19 @@ inline void gasprice(ExecutionState& state) noexcept state.stack.push(intx::be::load(state.host.get_tx_context().tx_gas_price)); } -inline void extcodesize(ExecutionState& state) noexcept +inline evmc_status_code extcodesize(ExecutionState& state) noexcept { auto& x = state.stack.top(); - x = state.host.get_code_size(intx::be::trunc(x)); + const auto addr = intx::be::trunc(x); + + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + + x = state.host.get_code_size(addr); + return EVMC_SUCCESS; } inline evmc_status_code extcodecopy(ExecutionState& state) noexcept @@ -418,6 +436,12 @@ inline evmc_status_code extcodecopy(ExecutionState& state) noexcept if ((state.gas_left -= copy_cost) < 0) return EVMC_OUT_OF_GAS; + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + auto data = s != 0 ? &state.memory[dst] : nullptr; auto num_bytes_copied = state.host.copy_code(addr, src, data, s); if (s - num_bytes_copied > 0) @@ -460,10 +484,19 @@ inline evmc_status_code returndatacopy(ExecutionState& state) noexcept return EVMC_SUCCESS; } -inline void extcodehash(ExecutionState& state) noexcept +inline evmc_status_code extcodehash(ExecutionState& state) noexcept { auto& x = state.stack.top(); - x = intx::be::load(state.host.get_code_hash(intx::be::trunc(x))); + const auto addr = intx::be::trunc(x); + + if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + + x = intx::be::load(state.host.get_code_hash(addr)); + return EVMC_SUCCESS; } From 97135272c638a0d0a1fcf6305968232bd3d13b94 Mon Sep 17 00:00:00 2001 From: yperbasis Date: Sun, 14 Feb 2021 16:52:43 +0100 Subject: [PATCH 5/8] Implement EIP-2929 for CALL* instructions --- lib/evmone/instructions_calls.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index fb9ac49e4e..c424a16ead 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -20,6 +20,12 @@ evmc_status_code call(ExecutionState& state) noexcept state.stack.push(0); // Assume failure. + if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + if (!check_memory(state, input_offset, input_size)) return EVMC_OUT_OF_GAS; From 75f99a4c950a65228962e4d6bb29f3f93055c97e Mon Sep 17 00:00:00 2001 From: yperbasis Date: Mon, 15 Feb 2021 11:44:13 +0100 Subject: [PATCH 6/8] Implement EIP-2929 for SELFDESTRUCT --- lib/evmone/instructions.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 4e5ad96a5f..f3d109255f 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -729,6 +729,12 @@ inline evmc_status_code selfdestruct(ExecutionState& state) noexcept const auto beneficiary = intx::be::trunc(state.stack[0]); + if (state.rev >= EVMC_BERLIN && state.host.access_account(beneficiary) == EVMC_ACCESS_COLD) + { + if ((state.gas_left -= instr::cold_account_access_cost) < 0) + return EVMC_OUT_OF_GAS; + } + if (state.rev >= EVMC_TANGERINE_WHISTLE) { if (state.rev == EVMC_TANGERINE_WHISTLE || state.host.get_balance(state.msg->destination)) From 0128d83a467101852759d86ba66c7beb3ffdfafa Mon Sep 17 00:00:00 2001 From: yperbasis Date: Thu, 11 Mar 2021 10:46:35 +0100 Subject: [PATCH 7/8] ci: Upgrade Silkworm and Ethereum tests (8.0.3) This uses temporary Silkworm version matching EVMC 8.0.0-alpha.2 ABI. --- circle.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 14541e9cf3..8dce37966c 100644 --- a/circle.yml +++ b/circle.yml @@ -19,7 +19,7 @@ commands: steps: - restore_cache: name: "Restore Silkworm cache" - key: &silkworm-cache-key silkworm-v1 + key: &silkworm-cache-key silkworm-20210407 - run: name: "Check Silkworm cache" command: | @@ -38,11 +38,11 @@ commands: working_directory: ~/silkworm/src command: | [ "$SILKWORM_BUILD" = true ] || exit 0 - git clone --no-checkout --single-branch https://github.com/torquem-ch/silkworm.git . - git checkout 614fabfcf19b8e06f0dcbe3e9af2345dcf748f3e + git clone --no-checkout --single-branch --branch eip2929_update https://github.com/torquem-ch/silkworm.git . + git checkout b083086cd4692b2a7f54af06644324a833fb4d3e git submodule init git submodule deinit tests - git submodule update --recursive --depth=1 --progress + git submodule update --init --recursive --depth=1 --progress - run: name: "Configure Silkworm" working_directory: ~/silkworm @@ -247,7 +247,7 @@ jobs: - build - build_silkworm - download_consensus_tests: - rev: v7.0.0 + rev: 8.0.3 - run: name: "Silkworm-driven consensus tests" working_directory: ~/build From a2e92ef8981b77da425d4cd91348941106d3d29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Apr 2021 11:57:37 +0200 Subject: [PATCH 8/8] test: Add EIP-2929 unit tests --- test/unittests/CMakeLists.txt | 1 + test/unittests/evm_eip2929_test.cpp | 258 ++++++++++++++++++++++++++++ test/unittests/evm_fixture.hpp | 6 + 3 files changed, 265 insertions(+) create mode 100644 test/unittests/evm_eip2929_test.cpp diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index ffb68ce4a0..bed9547dd6 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(evmone-unittests evm_fixture.hpp evm_test.cpp evm_calls_test.cpp + evm_eip2929_test.cpp evm_state_test.cpp evm_other_test.cpp evmone_test.cpp diff --git a/test/unittests/evm_eip2929_test.cpp b/test/unittests/evm_eip2929_test.cpp new file mode 100644 index 0000000000..56d9d1e954 --- /dev/null +++ b/test/unittests/evm_eip2929_test.cpp @@ -0,0 +1,258 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2021 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +/// This file contains EVM unit tests for EIP-2929 "Gas cost increases for state access opcodes" +/// https://eips.ethereum.org/EIPS/eip-2929 + +#include "evm_fixture.hpp" + +using namespace evmc::literals; +using evmone::test::evm; + +TEST_P(evm, eip2929_case1) +{ + // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-1 + rev = EVMC_BERLIN; + msg.sender = 0x0000000000000000000000000000000000000000_address; + msg.destination = 0x000000000000000000000000636F6E7472616374_address; + const auto code = + "0x60013f5060023b506003315060f13f5060f23b5060f3315060f23f5060f33b5060f1315032315030315000"; + + execute(code); + EXPECT_GAS_USED(EVMC_SUCCESS, 8653); + EXPECT_EQ(result.output_size, 0); + + const auto& r = host.recorded_account_accesses; + ASSERT_EQ(r.size(), 24); + EXPECT_EQ(r[0], msg.sender); + EXPECT_EQ(r[1], msg.destination); + EXPECT_EQ(r[2], 0x0000000000000000000000000000000000000001_address); + EXPECT_EQ(r[3], 0x0000000000000000000000000000000000000001_address); + EXPECT_EQ(r[4], 0x0000000000000000000000000000000000000002_address); + EXPECT_EQ(r[5], 0x0000000000000000000000000000000000000002_address); + EXPECT_EQ(r[6], 0x0000000000000000000000000000000000000003_address); + EXPECT_EQ(r[7], 0x0000000000000000000000000000000000000003_address); + EXPECT_EQ(r[8], 0x00000000000000000000000000000000000000f1_address); + EXPECT_EQ(r[9], 0x00000000000000000000000000000000000000f1_address); + EXPECT_EQ(r[10], 0x00000000000000000000000000000000000000f2_address); + EXPECT_EQ(r[11], 0x00000000000000000000000000000000000000f2_address); + EXPECT_EQ(r[12], 0x00000000000000000000000000000000000000f3_address); + EXPECT_EQ(r[13], 0x00000000000000000000000000000000000000f3_address); + EXPECT_EQ(r[14], 0x00000000000000000000000000000000000000f2_address); + EXPECT_EQ(r[15], 0x00000000000000000000000000000000000000f2_address); + EXPECT_EQ(r[16], 0x00000000000000000000000000000000000000f3_address); + EXPECT_EQ(r[17], 0x00000000000000000000000000000000000000f3_address); + EXPECT_EQ(r[18], 0x00000000000000000000000000000000000000f1_address); + EXPECT_EQ(r[19], 0x00000000000000000000000000000000000000f1_address); + EXPECT_EQ(r[20], 0x0000000000000000000000000000000000000000_address); + EXPECT_EQ(r[21], 0x0000000000000000000000000000000000000000_address); + EXPECT_EQ(r[22], msg.destination); + EXPECT_EQ(r[23], msg.destination); +} + +TEST_P(evm, eip2929_case2) +{ + // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-2 + rev = EVMC_BERLIN; + msg.sender = 0x0000000000000000000000000000000000000000_address; + msg.destination = 0x000000000000000000000000636F6E7472616374_address; + const auto code = "0x60006000600060ff3c60006000600060ff3c600060006000303c00"; + + execute(code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2835); + EXPECT_EQ(result.output_size, 0); + + const auto& r = host.recorded_account_accesses; + ASSERT_EQ(r.size(), 8); + EXPECT_EQ(r[0], msg.sender); + EXPECT_EQ(r[1], msg.destination); + EXPECT_EQ(r[2], 0x00000000000000000000000000000000000000ff_address); + EXPECT_EQ(r[3], 0x00000000000000000000000000000000000000ff_address); + EXPECT_EQ(r[4], 0x00000000000000000000000000000000000000ff_address); + EXPECT_EQ(r[5], 0x00000000000000000000000000000000000000ff_address); + EXPECT_EQ(r[6], msg.destination); + EXPECT_EQ(r[7], msg.destination); +} + +TEST_P(evm, eip2929_case3) +{ + // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-3 + rev = EVMC_BERLIN; + msg.sender = 0x0000000000000000000000000000000000000000_address; + msg.destination = 0x000000000000000000000000636F6E7472616374_address; + const auto code = "0x60015450601160015560116002556011600255600254600154"; + + execute(code); + EXPECT_GAS_USED(EVMC_SUCCESS, 44529); + EXPECT_EQ(result.output_size, 0); +} + +TEST_P(evm, eip2929_case4) +{ + // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-4 + rev = EVMC_BERLIN; + msg.sender = 0x0000000000000000000000000000000000000000_address; + msg.destination = 0x000000000000000000000000636F6E7472616374_address; + const auto code = "0x60008080808060046000f15060008080808060ff6000f15060008080808060ff6000fa50"; + + execute(code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2869); + EXPECT_EQ(result.output_size, 0); +} + +TEST_P(evm, eip2929_balance_oog) +{ + rev = EVMC_BERLIN; + const auto code = push(0x0a) + OP_BALANCE; + + execute(2603, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2603); + + host.recorded_account_accesses.clear(); + execute(2602, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2602); +} + +TEST_P(evm, eip2929_extcodesize_oog) +{ + rev = EVMC_BERLIN; + const auto code = push(0x0a) + OP_EXTCODESIZE; + + execute(2603, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2603); + + host.recorded_account_accesses.clear(); + execute(2602, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2602); +} + +TEST_P(evm, eip2929_extcodecopy_oog) +{ + rev = EVMC_BERLIN; + const auto code = push(0) + OP_DUP1 + OP_DUP1 + push(0x0a) + OP_EXTCODECOPY; + + execute(2612, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2612); + + host.recorded_account_accesses.clear(); + execute(2611, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2611); +} + +TEST_P(evm, eip2929_extcodehash_oog) +{ + rev = EVMC_BERLIN; + const auto code = push(0x0a) + OP_EXTCODEHASH; + + execute(2603, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2603); + + host.recorded_account_accesses.clear(); + execute(2602, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2602); +} + +TEST_P(evm, eip2929_sload_cold) +{ + rev = EVMC_BERLIN; + const auto code = push(1) + OP_SLOAD; + + const evmc::bytes32 key{1}; + host.accounts[msg.destination].storage[key] = evmc::bytes32{2}; + ASSERT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_COLD); + execute(2103, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2103); + EXPECT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_WARM); + + host.accounts[msg.destination].storage[key].access_status = EVMC_ACCESS_COLD; + execute(2102, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2102); +} + +TEST_P(evm, eip2929_sload_warm) +{ + rev = EVMC_BERLIN; + const auto code = push(1) + OP_SLOAD; + + const evmc::bytes32 key{1}; + host.accounts[msg.destination].storage[key] = {evmc::bytes32{2}, EVMC_ACCESS_WARM}; + ASSERT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_WARM); + execute(103, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 103); + EXPECT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_WARM); + + execute(102, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 102); +} + +TEST_P(evm, eip2929_sstore_modify_cold) +{ + rev = EVMC_BERLIN; + const auto code = sstore(1, 3); + + const evmc::bytes32 key{1}; + host.accounts[msg.destination].storage[key] = evmc::bytes32{2}; + execute(5006, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 5006); + EXPECT_EQ(host.accounts[msg.destination].storage[key].value, evmc::bytes32{3}); + EXPECT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_WARM); + + host.accounts[msg.destination].storage[key] = evmc::bytes32{2}; + execute(5005, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 5005); + // The storage will be modified anyway, because the cost is checked after. + EXPECT_EQ(host.accounts[msg.destination].storage[key].value, evmc::bytes32{3}); + EXPECT_EQ(host.accounts[msg.destination].storage[key].access_status, EVMC_ACCESS_WARM); +} + +TEST_P(evm, eip2929_selfdestruct_cold_beneficiary) +{ + rev = EVMC_BERLIN; + const auto code = push(0xbe) + OP_SELFDESTRUCT; + + execute(7603, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 7603); + + host.recorded_account_accesses.clear(); + execute(7602, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 7602); +} + +TEST_P(evm, eip2929_selfdestruct_warm_beneficiary) +{ + rev = EVMC_BERLIN; + const auto code = push(0xbe) + OP_SELFDESTRUCT; + + host.access_account(0x00000000000000000000000000000000000000be_address); + execute(5003, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 5003); + + host.recorded_account_accesses.clear(); + host.access_account(0x00000000000000000000000000000000000000be_address); + execute(5002, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 5002); +} + +TEST_P(evm, eip2929_delegatecall_cold) +{ + rev = EVMC_BERLIN; + const auto code = delegatecall(0xde); + auto& r = host.recorded_account_accesses; + + execute(2618, code); + EXPECT_GAS_USED(EVMC_SUCCESS, 2618); + ASSERT_EQ(r.size(), 4); + EXPECT_EQ(r[0], msg.sender); + EXPECT_EQ(r[1], msg.destination); + EXPECT_EQ(r[2], 0x00000000000000000000000000000000000000de_address); + EXPECT_EQ(r[3], 0x00000000000000000000000000000000000000de_address); + + r.clear(); + execute(2617, code); + EXPECT_GAS_USED(EVMC_OUT_OF_GAS, 2617); + ASSERT_EQ(r.size(), 3); + EXPECT_EQ(r[0], msg.sender); + EXPECT_EQ(r[1], msg.destination); + EXPECT_EQ(r[2], 0x00000000000000000000000000000000000000de_address); +} diff --git a/test/unittests/evm_fixture.hpp b/test/unittests/evm_fixture.hpp index 7c86e7e993..919158d017 100644 --- a/test/unittests/evm_fixture.hpp +++ b/test/unittests/evm_fixture.hpp @@ -72,6 +72,12 @@ class evm : public testing::TestWithParam msg.input_size = input.size(); msg.gas = gas; + if (rev >= EVMC_BERLIN) // Add EIP-2929 tweak. + { + host.access_account(msg.sender); + host.access_account(msg.destination); + } + result = vm.execute(host, rev, msg, code.data(), code.size()); output = {result.output_data, result.output_size}; gas_used = msg.gas - result.gas_left;