Skip to content

Commit

Permalink
Move delegation resolving to Host
Browse files Browse the repository at this point in the history
For EXTCODE* instructions do not follow delegation, but return sentinel values instead.
  • Loading branch information
gumb0 committed Nov 26, 2024
1 parent dcdb8c8 commit a892f86
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 89 deletions.
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 @@ constexpr auto EXTCALL_ABORT = 2;

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 @@ -544,4 +556,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
5 changes: 3 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,8 @@ 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;
}

Expand Down

0 comments on commit a892f86

Please sign in to comment.