From 612d896c889136cd57e1f720e5c47c662fa633a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Nov 2023 14:29:39 +0100 Subject: [PATCH 1/2] state: Implement EIP-6780 "SELFDESTRUCT" Implement the EIP-6780 "SELFDESTRUCT only in same transaction". https://eips.ethereum.org/EIPS/eip-6780 This EIP changes the functionality of the SELFDESTRUCT instruction. The new functionality will be only to send all Ether in the account to the beneficiary, except that the current behavior is preserved when SELFDESTRUCT is called in the same transaction a contract was created. --- test/state/account.hpp | 3 +++ test/state/host.cpp | 27 ++++++++++++++++++++++----- test/state/state.cpp | 5 ++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/test/state/account.hpp b/test/state/account.hpp index 242b4eca7e..a1b3e91947 100644 --- a/test/state/account.hpp +++ b/test/state/account.hpp @@ -53,6 +53,9 @@ struct Account /// or it is a newly created temporary account. bool erasable = false; + /// The account has been created in the current transaction. + bool just_created = false; + evmc_access_status access_status = EVMC_ACCESS_COLD; [[nodiscard]] bool is_empty() const noexcept diff --git a/test/state/host.cpp b/test/state/host.cpp index 4741757aad..6e29eaa8d0 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -100,12 +100,27 @@ size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_ bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcept { - // Touch beneficiary and transfer all balance to it. - // This may happen multiple times per single account as account's balance - // can be increased with a call following previous selfdestruct. auto& acc = m_state.get(addr); - m_state.touch(beneficiary).balance += acc.balance; - acc.balance = 0; // Zero balance (this can be the beneficiary). + const auto balance = acc.balance; + auto& beneficiary_acc = m_state.touch(beneficiary); + + if (m_rev >= EVMC_CANCUN && !acc.just_created) + { + // EIP-6780: + // "SELFDESTRUCT is executed in a transaction that is not the same + // as the contract invoking SELFDESTRUCT was created" + acc.balance = 0; + beneficiary_acc.balance += balance; // Keep balance if acc is the beneficiary. + + // Return "selfdestruct not registered". + // In practice this affects only refunds before Cancun. + return false; + } + + // Transfer may happen multiple times per single account as account's balance + // can be increased with a call following previous selfdestruct. + beneficiary_acc.balance += balance; + acc.balance = 0; // Zero balance if acc is the beneficiary. // Mark the destruction if not done already. return !std::exchange(acc.destructed, true); @@ -184,6 +199,8 @@ evmc::Result Host::create(const evmc_message& msg) noexcept if (m_rev >= EVMC_SPURIOUS_DRAGON) new_acc.nonce = 1; + new_acc.just_created = true; + // Clear the new account storage, but keep the access status (from tx access list). // This is only needed for tests and cannot happen in real networks. for (auto& [_, v] : new_acc.storage) [[unlikely]] diff --git a/test/state/state.cpp b/test/state/state.cpp index de7654133a..2b74a76b4f 100644 --- a/test/state/state.cpp +++ b/test/state/state.cpp @@ -254,11 +254,14 @@ std::variant transition(State& state, const if (rev >= EVMC_SPURIOUS_DRAGON) delete_empty_accounts(state); - // Set accounts and their storage access status to cold in the end of transition process + // Post-transaction clean-up. + // - Set accounts and their storage access status to cold. + // - Clear the "just created" account flag. for (auto& [addr, acc] : state.get_accounts()) { acc.transient_storage.clear(); acc.access_status = EVMC_ACCESS_COLD; + acc.just_created = false; for (auto& [key, val] : acc.storage) { val.access_status = EVMC_ACCESS_COLD; From 90f4f801ef57660ecda6260adbbc294498781d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 24 Nov 2023 13:24:03 +0100 Subject: [PATCH 2/2] test: Add unit tests for EIP-6780 --- test/unittests/CMakeLists.txt | 1 + .../state_transition_selfdestruct_test.cpp | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/unittests/state_transition_selfdestruct_test.cpp diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index a08098fa96..4e74c518bd 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -51,6 +51,7 @@ target_sources( state_transition_block_test.cpp state_transition_create_test.cpp state_transition_eof_test.cpp + state_transition_selfdestruct_test.cpp state_transition_trace_test.cpp state_transition_transient_storage_test.cpp state_transition_tx_test.cpp diff --git a/test/unittests/state_transition_selfdestruct_test.cpp b/test/unittests/state_transition_selfdestruct_test.cpp new file mode 100644 index 0000000000..aa2253533c --- /dev/null +++ b/test/unittests/state_transition_selfdestruct_test.cpp @@ -0,0 +1,48 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "../utils/bytecode.hpp" +#include "state_transition.hpp" + +using namespace evmc::literals; +using namespace evmone::test; + +TEST_F(state_transition, selfdestruct_shanghai) +{ + rev = EVMC_SHANGHAI; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)}); + + expect.post[To].exists = false; + expect.post[0xbe_address].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_cancun) +{ + rev = EVMC_CANCUN; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)}); + + expect.post[To].balance = 0; + expect.post[0xbe_address].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_to_self_cancun) +{ + rev = EVMC_CANCUN; + tx.to = To; + pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(To)}); + + expect.post[To].balance = 0x4e; +} + +TEST_F(state_transition, selfdestruct_same_tx_cancun) +{ + rev = EVMC_CANCUN; + tx.value = 0x4e; + tx.data = selfdestruct(0xbe_address); + pre.get(Sender).balance += 0x4e; + + expect.post[0xbe_address].balance = 0x4e; +}