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 #961

Merged
merged 11 commits into from
Feb 13, 2025
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: 2 additions & 0 deletions lib/evmone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ add_library(evmone
baseline_instruction_table.cpp
baseline_instruction_table.hpp
constants.hpp
delegation.cpp
delegation.hpp
eof.cpp
eof.hpp
instructions.hpp
Expand Down
29 changes: 29 additions & 0 deletions lib/evmone/delegation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2025 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#include "delegation.hpp"
#include <cassert>

namespace evmone
{
std::optional<evmc::address> get_delegate_address(
const evmc::HostInterface& host, const evmc::address& addr) noexcept
{
// Load the code prefix up to the delegation designation size.
// The HostInterface::copy_code() copies up to the addr's code size
// and returns the number of bytes copied.
uint8_t designation_buffer[std::size(DELEGATION_MAGIC) + sizeof(evmc::address)];
const auto size = host.copy_code(addr, 0, designation_buffer, std::size(designation_buffer));
const bytes_view designation{designation_buffer, size};

if (!is_code_delegated(designation))
return {};

// Copy the delegate address from the designation buffer.
evmc::address delegate_address;
// Assume the designation with the valid magic has also valid length.
assert(designation.size() == std::size(designation_buffer));
std::ranges::copy(designation.substr(std::size(DELEGATION_MAGIC)), delegate_address.bytes);
return delegate_address;
}
} // namespace evmone
28 changes: 28 additions & 0 deletions lib/evmone/delegation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 <evmc/utils.h>

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
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.
EVMC_EXPORT std::optional<evmc::address> get_delegate_address(
const evmc::HostInterface& host, const evmc::address& addr) noexcept;
} // namespace evmone
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(state.host, addr);
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.
/// NOLINTNEXTLINE(misc-use-internal-linkage) fixed in clang-tidy 20.
consteval evmc_call_kind to_call_kind(Opcode op) noexcept
Expand Down Expand Up @@ -67,6 +96,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 101 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L101

Added line #L101 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 @@ -80,9 +115,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};
chfast marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -178,6 +217,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 222 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L222

Added line #L222 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 @@ -186,9 +231,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 235 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L235

Added line #L235 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
3 changes: 2 additions & 1 deletion test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ evmc::Result Host::execute_message(const evmc_message& msg) noexcept
}
}

if (is_precompile(m_rev, msg.code_address))
// Calls to precompile address via EIP-7702 delegation execute empty code instead of precompile.
if ((msg.flags & EVMC_DELEGATED) == 0 && is_precompile(m_rev, msg.code_address))
chfast marked this conversation as resolved.
Show resolved Hide resolved
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