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: Make EXTCODE* return sentinel values for delegated accounts [devnet-5 spec update] #1072

Open
wants to merge 2 commits into
base: eip-7702
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
2 changes: 1 addition & 1 deletion evmc
6 changes: 0 additions & 6 deletions lib/evmone/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,4 @@ constexpr auto MAX_CODE_SIZE = 0x6000;
/// The limit of the size of init codes for contract creation
/// defined by [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860)
constexpr auto MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE;

/// 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)};

} // namespace evmone
76 changes: 3 additions & 73 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,55 +129,6 @@ inline bool check_memory(
return check_memory(gas_left, memory, offset, static_cast<uint64_t>(size));
}

/// 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.
inline std::optional<evmc::address> get_delegate_address(
const evmc::address& addr, const evmc::HostContext& 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;
}

/// Get target address of an instruction with address argument.
///
/// 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.has_value())
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};

return *delegate_addr;
}

namespace instr::core
{

Expand Down Expand Up @@ -565,7 +516,6 @@ inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept
stack.push(intx::be::load<uint256>(state.get_tx_context().blob_base_fee));
}

// NOLINTNEXTLINE(bugprone-exception-escape)
inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
auto& x = stack.top();
Expand All @@ -577,17 +527,10 @@ inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& stat
return {EVMC_OUT_OF_GAS, gas_left};
}

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

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

x = state.host.get_code_size(target_addr);
x = state.host.get_code_size(addr);
return {EVMC_SUCCESS, gas_left};
}

// NOLINTNEXTLINE(bugprone-exception-escape)
inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
const auto addr = intx::be::trunc<evmc::address>(stack.pop());
Expand All @@ -608,18 +551,12 @@ inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& stat
return {EVMC_OUT_OF_GAS, gas_left};
}

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

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

if (s > 0)
{
const auto src =
(max_buffer_size < input_index) ? max_buffer_size : static_cast<size_t>(input_index);
const auto dst = static_cast<size_t>(mem_index);
const auto num_bytes_copied = state.host.copy_code(target_addr, src, &state.memory[dst], s);
const auto num_bytes_copied = state.host.copy_code(addr, src, &state.memory[dst], s);
if (const auto num_bytes_to_clear = s - num_bytes_copied; num_bytes_to_clear > 0)
std::memset(&state.memory[dst + num_bytes_copied], 0, num_bytes_to_clear);
}
Expand Down Expand Up @@ -697,7 +634,6 @@ inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& s
return {EVMC_SUCCESS, gas_left};
}

// NOLINTNEXTLINE(bugprone-exception-escape)
inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
auto& x = stack.top();
Expand All @@ -709,13 +645,7 @@ inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& stat
return {EVMC_OUT_OF_GAS, gas_left};
}

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

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

x = intx::be::load<uint256>(state.host.get_code_hash(target_addr));
x = intx::be::load<uint256>(state.host.get_code_hash(addr));
return {EVMC_SUCCESS, gas_left};
}

Expand Down
28 changes: 28 additions & 0 deletions lib/evmone/instructions_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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 = state.host.get_delegate_address(addr);
if (delegate_addr == evmc::address{})
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 41 in lib/evmone/instructions_calls.cpp

View check run for this annotation

Codecov / codecov/patch

lib/evmone/instructions_calls.cpp#L41

Added line #L41 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
29 changes: 27 additions & 2 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

namespace evmone::state
{
namespace
{
/// The value returned by EXTCODEHASH of an address with EIP-7702 delegation designator.
/// See https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
constexpr auto EIP7702_CODE_HASH_SENTINEL =
0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329_bytes32;
} // namespace

bool Host::account_exists(const address& addr) const noexcept
{
const auto* const acc = m_state.find(addr);
Expand Down Expand Up @@ -81,7 +89,7 @@ namespace
/// 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;
return (is_eof_container(code) || is_code_delegated(code)) ? code.substr(0, 2) : code;
}

/// Check if an existing account is the "create collision"
Expand Down Expand Up @@ -120,9 +128,13 @@ 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;

if (is_code_delegated(code))
return EIP7702_CODE_HASH_SENTINEL;

return acc->code_hash;
}

Expand Down Expand Up @@ -547,4 +559,17 @@ void Host::set_transient_storage(
m_state.journal_transient_storage_change(addr, key, slot);
slot = value;
}

address Host::get_delegate_address(const address& addr) const noexcept
{
const auto raw_code = m_state.get_code(addr);

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

address delegate;
assert(raw_code.size() == std::size(DELEGATION_MAGIC) + sizeof(delegate));
std::copy_n(&raw_code[std::size(DELEGATION_MAGIC)], sizeof(delegate), delegate.bytes);
return delegate;
}
} // namespace evmone::state
2 changes: 2 additions & 0 deletions test/state/host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class Host : public evmc::Host
void emit_log(const address& addr, const uint8_t* data, size_t data_size,
const bytes32 topics[], size_t topics_count) noexcept override;

address get_delegate_address(const address& addr) const noexcept override;

public:
evmc_access_status access_account(const address& addr) noexcept override;

Expand Down
5 changes: 0 additions & 5 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ evmc_message build_message(
.code = nullptr,
.code_size = 0};
}

constexpr bool is_code_delegated(bytes_view code) noexcept
{
return code.starts_with(DELEGATION_MAGIC);
}
} // namespace

StateDiff State::build_diff(evmc_revision rev) const
Expand Down
11 changes: 11 additions & 0 deletions test/state/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,15 @@ TransactionReceipt transition(const StateView& state, const BlockInfo& block,
[[nodiscard]] std::variant<int64_t, std::error_code> validate_transaction(
const StateView& state_view, const BlockInfo& block, const Transaction& tx, evmc_revision rev,
int64_t block_gas_left, int64_t blob_gas_left) noexcept;

/// 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);
}
} // namespace evmone::state
23 changes: 21 additions & 2 deletions test/unittests/state_transition_eip7702_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ TEST_F(state_transition, eip7702_extcodesize)

expect.post[callee].exists = true;
expect.post[delegate].exists = true;
expect.post[To].storage[0x01_bytes32] = 0x0400_bytes32;
expect.post[To].storage[0x01_bytes32] = 0x02_bytes32;
}

TEST_F(state_transition, eip7702_extcodehash_delegation_to_empty)
Expand All @@ -87,7 +87,26 @@ TEST_F(state_transition, eip7702_extcodehash_delegation_to_empty)

expect.post[callee].exists = true;
expect.post[delegate].exists = false;
expect.post[To].storage[0x00_bytes32] = 0x00_bytes32;
expect.post[To].storage[0x00_bytes32] =
0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329_bytes32;
expect.post[To].storage[0x01_bytes32] = 0x01_bytes32;
}

TEST_F(state_transition, eip7702_extcodecopy)
{
rev = EVMC_PRAGUE;

constexpr auto callee = 0xca11ee_address;
constexpr auto delegate = 0xde1e_address;
pre[callee] = {.nonce = 1, .code = bytes{0xef, 0x01, 0x00} + hex(delegate)};
tx.to = To;
pre[To] = {.code = push(10) + push0() + push0() + push(callee) + OP_EXTCODECOPY +
sstore(0, mload(0)) + sstore(1, 1)};

expect.post[callee].exists = true;
expect.post[delegate].exists = false;
expect.post[To].storage[0x00_bytes32] =
0xef01000000000000000000000000000000000000000000000000000000000000_bytes32;
expect.post[To].storage[0x01_bytes32] = 0x01_bytes32;
}

Expand Down