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

host: Make EOF opaque for EXTCODE* instructions #587

Merged
merged 5 commits into from
Mar 13, 2024
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
2 changes: 1 addition & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
17 changes: 14 additions & 3 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,35 @@ uint256be Host::get_balance(const address& addr) const noexcept
return (acc != nullptr) ? intx::be::store<uint256be>(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{};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patching EXTCODEHASH will be a headache for real implementations. The code hash is available directly in the account node. Now you need to additionally load the code to obtain a possibly different hash.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now you need to additionally load the code to obtain a possibly different hash.

This was always the case with this approach. Need to figure out if it is an EOF account or not (by loading the account).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keccak256("ef00") should better be memoized or hard-coded

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is generally fine for now although it conflicts with the TODO comment above.

}

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);
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions test/unittests/state_transition_extcode_test.cpp
Original file line number Diff line number Diff line change
@@ -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;
}