diff --git a/circle.yml b/circle.yml index d9808b6910..097dc3fa10 100644 --- a/circle.yml +++ b/circle.yml @@ -536,7 +536,7 @@ jobs: ~/tests/EIPTests/BlockchainTests/ - download_execution_tests: repo: ipsilon/tests - rev: eof-relaxed-stack-validation-20240221 + rev: update-extcode legacy: false - run: name: "State tests (EOF)" diff --git a/test/state/host.cpp b/test/state/host.cpp index be4df52634..8852d26ad5 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -77,24 +77,35 @@ uint256be Host::get_balance(const address& addr) const noexcept return (acc != nullptr) ? intx::be::store(acc->balance) : uint256be{}; } +namespace +{ +/// For EXTCODE* instructions if the target is an EOF account, then only return EF00. +/// While we only do this if the caller is legacy, it is not a problem doing this +/// unconditionally, because EOF contracts dot no have EXTCODE* instructions. +bytes_view extcode(bytes_view code) noexcept +{ + return is_eof_container(code) ? code.substr(0, 2) : code; +} +} // namespace + size_t Host::get_code_size(const address& addr) const noexcept { const auto* const acc = m_state.find(addr); - return (acc != nullptr) ? acc->code.size() : 0; + return (acc != nullptr) ? extcode(acc->code).size() : 0; } bytes32 Host::get_code_hash(const address& addr) const noexcept { // TODO: Cache code hash. It will be needed also to compute the MPT hash. const auto* const acc = m_state.find(addr); - return (acc != nullptr && !acc->is_empty()) ? keccak256(acc->code) : bytes32{}; + return (acc != nullptr && !acc->is_empty()) ? keccak256(extcode(acc->code)) : bytes32{}; } size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) const noexcept { const auto* const acc = m_state.find(addr); - const auto code = (acc != nullptr) ? bytes_view{acc->code} : bytes_view{}; + const auto code = (acc != nullptr) ? extcode(acc->code) : bytes_view{}; const auto code_slice = code.substr(std::min(code_offset, code.size())); const auto num_bytes = std::min(buffer_size, code_slice.size()); std::copy_n(code_slice.begin(), num_bytes, buffer_data); diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index a0b43159cc..f2537f6d33 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -58,6 +58,7 @@ target_sources( state_transition_block_test.cpp state_transition_create_test.cpp state_transition_eof_test.cpp + state_transition_extcode_test.cpp state_transition_selfdestruct_test.cpp state_transition_trace_test.cpp state_transition_transient_storage_test.cpp diff --git a/test/unittests/state_transition_extcode_test.cpp b/test/unittests/state_transition_extcode_test.cpp new file mode 100644 index 0000000000..f97dbced4c --- /dev/null +++ b/test/unittests/state_transition_extcode_test.cpp @@ -0,0 +1,54 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +constexpr auto target = 0xfffffffffffffffffffffffffffffffffffffffe_address; + +TEST_F(state_transition, legacy_extcodesize_eof) +{ + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(push(target) + sstore(1, OP_EXTCODESIZE)), + }); + expect.post[*tx.to].storage[0x01_bytes32] = 0x02_bytes32; + expect.post[target].exists = true; +} + +TEST_F(state_transition, legacy_extcodehash_eof) +{ + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(push(target) + sstore(1, OP_EXTCODEHASH)), + }); + expect.post[*tx.to].storage[0x01_bytes32] = keccak256(bytecode("EF00")); + expect.post[target].exists = true; +} + +TEST_F(state_transition, legacy_extcodecopy_eof) +{ + constexpr auto ones = + 0x1111111111111111111111111111111111111111111111111111111111111111_bytes32; + pre.insert(target, {.code = eof_bytecode("FE")}); + + rev = EVMC_PRAGUE; + tx.to = To; + pre.insert(*tx.to, { + .code = bytecode(mstore(0, ones) + push(20) + push0() + push0() + + push(target) + OP_EXTCODECOPY + sstore(1, mload(0))), + }); + expect.post[*tx.to].storage[0x01_bytes32] = + 0xef00000000000000000000000000000000000000111111111111111111111111_bytes32; + expect.post[target].exists = true; +}