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-7702: Set EOA account code [devnet-6 version] #961

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 1 addition & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -373,14 +373,13 @@ jobs:
working_directory: ~/spec-tests/fixtures/state_tests
command: >
~/build/bin/evmone-statetest ~/spec-tests/fixtures/state_tests
--gtest_filter='-*eip7702*'
- run:
name: "Execution spec tests (develop, blockchain_tests)"
# Tests for in-development EVM revision currently passing.
working_directory: ~/spec-tests/fixtures/blockchain_tests
command: >
~/build/bin/evmone-blockchaintest ~/spec-tests/fixtures/blockchain_tests
--gtest_filter='-*block_hashes.block_hashes_history:*eip7702*:*eip7623*'
--gtest_filter='-*block_hashes.block_hashes_history'
- collect_coverage_gcc
- upload_coverage:
flags: execution_spec_tests
Expand Down
2 changes: 1 addition & 1 deletion evmc
1 change: 1 addition & 0 deletions lib/evmone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_library(evmone
baseline_instruction_table.cpp
baseline_instruction_table.hpp
constants.hpp
delegation.hpp
eof.cpp
eof.hpp
instructions.hpp
Expand Down
42 changes: 42 additions & 0 deletions lib/evmone/delegation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2025 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <evmc/bytes.hpp>
#include <evmc/evmc.hpp>
#include <cassert>

namespace evmone
{
using evmc::bytes_view;

/// Prefix of code for delegated accounts
/// defined by [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)
constexpr uint8_t DELEGATION_MAGIC_BYTES[] = {0xef, 0x01, 0x00};
constexpr bytes_view DELEGATION_MAGIC{DELEGATION_MAGIC_BYTES, std::size(DELEGATION_MAGIC_BYTES)};

/// Check if code contains EIP-7702 delegation designator
inline constexpr bool is_code_delegated(bytes_view code) noexcept
{
return code.starts_with(DELEGATION_MAGIC);
}

/// Get EIP-7702 delegate address from the code of addr, if it is delegated.
inline std::optional<evmc::address> get_delegate_address(
const evmc::address& addr, const evmc::HostInterface& host) noexcept
{
uint8_t prefix[std::size(DELEGATION_MAGIC)] = {};
host.copy_code(addr, 0, prefix, std::size(prefix));

if (!is_code_delegated(bytes_view{prefix, std::size(prefix)}))
return {};

evmc::address delegate_address;
assert(host.get_code_size(addr) ==
std::size(DELEGATION_MAGIC) + std::size(delegate_address.bytes));
host.copy_code(
addr, std::size(prefix), delegate_address.bytes, std::size(delegate_address.bytes));
return delegate_address;
}
} // namespace evmone
1 change: 1 addition & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "baseline.hpp"
#include "constants.hpp"
#include "eof.hpp"
#include "execution_state.hpp"
#include "instructions_traits.hpp"
Expand Down
53 changes: 51 additions & 2 deletions lib/evmone/instructions_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2019 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "delegation.hpp"
#include "eof.hpp"
#include "instructions.hpp"

Expand All @@ -16,6 +17,34 @@

namespace evmone::instr::core
{
namespace
{
/// Get target address of a code executing instruction.
///
/// Returns EIP-7702 delegate address if addr is delegated, or addr itself otherwise.
/// Applies gas charge for accessing delegate account and may fail with out of gas.
inline std::variant<evmc::address, Result> get_target_address(
const evmc::address& addr, int64_t& gas_left, ExecutionState& state) noexcept
{
if (state.rev < EVMC_PRAGUE)
return addr;

const auto delegate_addr = get_delegate_address(addr, state.host);
if (!delegate_addr)
return addr;

const auto delegate_account_access_cost =
(state.host.access_account(*delegate_addr) == EVMC_ACCESS_COLD ?
instr::cold_account_access_cost :
instr::warm_storage_read_cost);

if ((gas_left -= delegate_account_access_cost) < 0)
return Result{EVMC_OUT_OF_GAS, gas_left};

Check warning on line 42 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L42

Added line #L42 was not covered by tests

return *delegate_addr;
}
} // namespace

/// Converts an opcode to matching EVMC call kind.
consteval evmc_call_kind to_call_kind(Opcode op) noexcept
{
Expand Down Expand Up @@ -66,6 +95,12 @@
return {EVMC_OUT_OF_GAS, gas_left};
}

const auto target_addr_or_result = get_target_address(dst, gas_left, state);
if (const auto* result = std::get_if<Result>(&target_addr_or_result))
return *result;

Check warning on line 100 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L100

Added line #L100 was not covered by tests

const auto& code_addr = std::get<evmc::address>(target_addr_or_result);

if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256))
return {EVMC_OUT_OF_GAS, gas_left};

Expand All @@ -79,9 +114,13 @@

evmc_message msg{.kind = to_call_kind(Op)};
msg.flags = (Op == OP_STATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags;
if (dst != code_addr)
msg.flags |= EVMC_DELEGATED;
else
msg.flags &= ~std::underlying_type_t<evmc_flags>{EVMC_DELEGATED};
Copy link
Member Author

@gumb0 gumb0 Sep 19, 2024

Choose a reason for hiding this comment

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

This hack points to that maybe msg.flags is not the best way to signal delegation (because it is not propagated unlike static mode).

Alternative could be a separate delegation address field in evmc_message, or another flag field.

msg.depth = state.msg->depth + 1;
msg.recipient = (Op == OP_CALL || Op == OP_STATICCALL) ? dst : state.msg->recipient;
msg.code_address = dst;
msg.code_address = code_addr;
msg.sender = (Op == OP_DELEGATECALL) ? state.msg->sender : state.msg->recipient;
msg.value =
(Op == OP_DELEGATECALL) ? state.msg->value : intx::be::store<evmc::uint256be>(value);
Expand Down Expand Up @@ -177,6 +216,12 @@
return {EVMC_OUT_OF_GAS, gas_left};
}

const auto target_addr_or_result = get_target_address(dst, gas_left, state);
if (const auto* result = std::get_if<Result>(&target_addr_or_result))
return *result;

Check warning on line 221 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L221

Added line #L221 was not covered by tests

const auto& code_addr = std::get<evmc::address>(target_addr_or_result);

if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256))
return {EVMC_OUT_OF_GAS, gas_left};

Expand All @@ -185,9 +230,13 @@

evmc_message msg{.kind = to_call_kind(Op)};
msg.flags = (Op == OP_EXTSTATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags;
if (dst != code_addr)
msg.flags |= EVMC_DELEGATED;

Check warning on line 234 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L234

Added line #L234 was not covered by tests
else
msg.flags &= ~std::underlying_type_t<evmc_flags>{EVMC_DELEGATED};
msg.depth = state.msg->depth + 1;
msg.recipient = (Op != OP_EXTDELEGATECALL) ? dst : state.msg->recipient;
msg.code_address = dst;
msg.code_address = code_addr;
msg.sender = (Op == OP_EXTDELEGATECALL) ? state.msg->sender : state.msg->recipient;
msg.value =
(Op == OP_EXTDELEGATECALL) ? state.msg->value : intx::be::store<evmc::uint256be>(value);
Expand Down
6 changes: 6 additions & 0 deletions test/state/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
EMPTY_BLOB_HASHES_LIST,
INVALID_BLOB_HASH_VERSION,
BLOB_GAS_LIMIT_EXCEEDED,
CREATE_SET_CODE_TX,
EMPTY_AUTHORIZATION_LIST,
UNKNOWN_ERROR,
};

Expand Down Expand Up @@ -73,6 +75,10 @@
return "invalid blob hash version";
case BLOB_GAS_LIMIT_EXCEEDED:
return "blob gas limit exceeded";
case CREATE_SET_CODE_TX:
return "set code transaction must not be a create transaction";
case EMPTY_AUTHORIZATION_LIST:
return "empty authorization list";

Check warning on line 81 in test/state/errors.hpp

View check run for this annotation

Codecov / codecov/patch

test/state/errors.hpp#L80-L81

Added lines #L80 - L81 were not covered by tests
case UNKNOWN_ERROR:
return "Unknown error";
default:
Expand Down
5 changes: 3 additions & 2 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ bytes32 Host::get_code_hash(const address& addr) const noexcept

// Load code and check if not EOF.
// TODO: Optimize the second account lookup here.
if (is_eof_container(m_state.get_code(addr)))
const auto code = m_state.get_code(addr);
if (is_eof_container(code))
return EOF_CODE_HASH_SENTINEL;

return acc->code_hash;
Expand Down Expand Up @@ -439,7 +440,7 @@ evmc::Result Host::execute_message(const evmc_message& msg) noexcept
}
}

if (is_precompile(m_rev, msg.code_address))
if (is_precompile(m_rev, msg.code_address) && (msg.flags & EVMC_DELEGATED) == 0)
return call_precompile(m_rev, msg);

// TODO: get_code() performs the account lookup. Add a way to get an account with code?
Expand Down
1 change: 1 addition & 0 deletions test/state/precompiles_stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ ExecutionResult expmod_stub(
"0000000000000000000000000000000000000000000000000000000000000000"_hex},
{0xd09419104ce1c64b6a06bcf063e98c2c91ad9e1beaf98b21c9d4734b4a3c9956_bytes32,
"0000000000000000000000000000000000000000000000000000000000000000"_hex},
{0xd397b3b043d87fcd6fad1291ff0bfd16401c274896d8c63a923727f077b8e0b5_bytes32, ""_hex},
{0xd6c0c03ec1f713b63be3d39b4fa8ef082b3407adc29baf74669fd2a574c638ac_bytes32, "01"_hex},
{0xd837f9dcf93155fe558c02c7a660edc0cd238a8b8f95ee6b68e4a5c6a41fc70a_bytes32,
"0000000000000000000000000000000000000000000000000000000000000001"_hex},
Expand Down
Loading