diff --git a/circle.yml b/circle.yml index 1b31c97013..e4bb4f83fb 100644 --- a/circle.yml +++ b/circle.yml @@ -367,8 +367,8 @@ jobs: --gtest_filter='*:-*eip4844*:*eip4788*' ~/spec-tests/fixtures/blockchain_tests - download_execution_spec_tests: - release: pectra-devnet-4@v1.0.1 - fixtures_suffix: pectra-devnet-4 + release: pectra-devnet-5@v1.1.0 + fixtures_suffix: pectra-devnet-5 - run: name: "Execution spec tests (develop, state_tests)" # Tests for in-development EVM revision currently passing. diff --git a/evmc b/evmc index 37b008414c..9badaa3bc0 160000 --- a/evmc +++ b/evmc @@ -1 +1 @@ -Subproject commit 37b008414c260e941a784306991ac03e4b67a7de +Subproject commit 9badaa3bc06bf59953e6c11f17e1cd0d3d74b208 diff --git a/lib/evmone/constants.hpp b/lib/evmone/constants.hpp index 00b47d189a..f3200c0cd7 100644 --- a/lib/evmone/constants.hpp +++ b/lib/evmone/constants.hpp @@ -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 diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 674f337d53..2ce2e6430a 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -129,55 +129,6 @@ inline bool check_memory( return check_memory(gas_left, memory, offset, static_cast(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 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 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 { @@ -565,7 +516,6 @@ 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(); @@ -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(&target_addr_or_result)) - return *result; - - const auto& target_addr = std::get(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(stack.pop()); @@ -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(&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(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); } @@ -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(); @@ -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(&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)); + x = intx::be::load(state.host.get_code_hash(addr)); return {EVMC_SUCCESS, gas_left}; } diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index a28d6eb5ee..4951690a2c 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -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 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}; + + return delegate_addr; +} +} // namespace + /// Converts an opcode to matching EVMC call kind. consteval evmc_call_kind to_call_kind(Opcode op) noexcept { diff --git a/test/state/host.cpp b/test/state/host.cpp index 93319ed445..83b469542d 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -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); @@ -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" @@ -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; } @@ -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 diff --git a/test/state/host.hpp b/test/state/host.hpp index 9b911d67bb..2418db6379 100644 --- a/test/state/host.hpp +++ b/test/state/host.hpp @@ -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; diff --git a/test/state/state.cpp b/test/state/state.cpp index 95e4520caf..dfffe9d918 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -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 diff --git a/test/state/state.hpp b/test/state/state.hpp index 40ae0251ac..fc20aecdda 100644 --- a/test/state/state.hpp +++ b/test/state/state.hpp @@ -150,4 +150,15 @@ TransactionReceipt transition(const StateView& state, const BlockInfo& block, [[nodiscard]] std::variant 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 diff --git a/test/unittests/state_transition_eip7702_test.cpp b/test/unittests/state_transition_eip7702_test.cpp index 0be9abfce2..c98b81d770 100644 --- a/test/unittests/state_transition_eip7702_test.cpp +++ b/test/unittests/state_transition_eip7702_test.cpp @@ -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) @@ -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; }