From 42e7fe24d0a3f0578c42dfbcdd50eb6441e9b7cd Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 1 Aug 2024 12:09:51 +0200 Subject: [PATCH] Support code delegation in instructions implementation --- lib/evmone/instructions.hpp | 70 +++++++++++++++++++++++++++++-- lib/evmone/instructions_calls.cpp | 16 ++++++- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 6d16508c9f..2f25657aae 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -4,6 +4,7 @@ #pragma once #include "baseline.hpp" +#include "constants.hpp" #include "eof.hpp" #include "execution_state.hpp" #include "instructions_traits.hpp" @@ -128,6 +129,48 @@ inline bool check_memory( return check_memory(gas_left, memory, offset, static_cast(size)); } +constexpr bool is_code_delegated(bytes_view code) noexcept +{ + return code.starts_with(DELEGATION_MAGIC); +} + +inline std::optional 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 {}; + + assert(host.get_code_size(addr) == std::size(DELEGATION_MAGIC) + std::size(addr.bytes)); + + evmc::address delegate_address; + host.copy_code( + addr, std::size(prefix), delegate_address.bytes, std::size(delegate_address.bytes)); + return delegate_address; +} + +inline std::variant 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 account_access_cost = + (state.host.access_account(addr) == EVMC_ACCESS_COLD ? instr::cold_account_access_cost : + instr::warm_storage_read_cost); + + if ((gas_left -= account_access_cost) < 0) + return Result{EVMC_OUT_OF_GAS, gas_left}; + + return *delegate_addr; +} + namespace instr::core { @@ -515,6 +558,7 @@ inline void blobbasefee(StackTop stack, ExecutionState& state) noexcept stack.push(intx::be::load(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(); @@ -526,10 +570,17 @@ inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& stat return {EVMC_OUT_OF_GAS, gas_left}; } - x = state.host.get_code_size(addr); + const auto target_addr_or_result = get_target_address(addr, gas_left, state); + if (const auto* result = std::get_if(&target_addr_or_result)) + return *result; + + const auto& target_addr = std::get(target_addr_or_result); + + x = state.host.get_code_size(target_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(stack.pop()); @@ -550,12 +601,18 @@ 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(&target_addr_or_result)) + return *result; + + const auto& target_addr = std::get(target_addr_or_result); + if (s > 0) { const auto src = (max_buffer_size < input_index) ? max_buffer_size : static_cast(input_index); const auto dst = static_cast(mem_index); - const auto num_bytes_copied = state.host.copy_code(addr, src, &state.memory[dst], s); + const auto num_bytes_copied = state.host.copy_code(target_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); } @@ -633,6 +690,7 @@ 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(); @@ -644,7 +702,13 @@ inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& stat return {EVMC_OUT_OF_GAS, gas_left}; } - x = intx::be::load(state.host.get_code_hash(addr)); + const auto target_addr_or_result = get_target_address(addr, gas_left, state); + if (const auto* result = std::get_if(&target_addr_or_result)) + return *result; + + const auto& target_addr = std::get(target_addr_or_result); + + x = intx::be::load(state.host.get_code_hash(target_addr)); return {EVMC_SUCCESS, gas_left}; } diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index a58e046d67..cf9b6e8304 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -67,6 +67,12 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce 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(&target_addr_or_result)) + return *result; + + const auto& code_addr = std::get(target_addr_or_result); + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) return {EVMC_OUT_OF_GAS, gas_left}; @@ -82,7 +88,7 @@ Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexce msg.flags = (Op == OP_STATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; 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(value); @@ -178,6 +184,12 @@ Result extcall_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noe 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(&target_addr_or_result)) + return *result; + + const auto& code_addr = std::get(target_addr_or_result); + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) return {EVMC_OUT_OF_GAS, gas_left}; @@ -188,7 +200,7 @@ Result extcall_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noe msg.flags = (Op == OP_EXTSTATICCALL) ? uint32_t{EVMC_STATIC} : state.msg->flags; 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(value);