Skip to content

Commit 2fb7cc1

Browse files
authored
Implement EIP-6780 "SELFDESTRUCT" (#735)
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.
2 parents 0174b53 + 90f4f80 commit 2fb7cc1

File tree

5 files changed

+78
-6
lines changed

5 files changed

+78
-6
lines changed

test/state/account.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ struct Account
5353
/// or it is a newly created temporary account.
5454
bool erasable = false;
5555

56+
/// The account has been created in the current transaction.
57+
bool just_created = false;
58+
5659
evmc_access_status access_status = EVMC_ACCESS_COLD;
5760

5861
[[nodiscard]] bool is_empty() const noexcept

test/state/host.cpp

+22-5
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,27 @@ size_t Host::copy_code(const address& addr, size_t code_offset, uint8_t* buffer_
100100

101101
bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcept
102102
{
103-
// Touch beneficiary and transfer all balance to it.
104-
// This may happen multiple times per single account as account's balance
105-
// can be increased with a call following previous selfdestruct.
106103
auto& acc = m_state.get(addr);
107-
m_state.touch(beneficiary).balance += acc.balance;
108-
acc.balance = 0; // Zero balance (this can be the beneficiary).
104+
const auto balance = acc.balance;
105+
auto& beneficiary_acc = m_state.touch(beneficiary);
106+
107+
if (m_rev >= EVMC_CANCUN && !acc.just_created)
108+
{
109+
// EIP-6780:
110+
// "SELFDESTRUCT is executed in a transaction that is not the same
111+
// as the contract invoking SELFDESTRUCT was created"
112+
acc.balance = 0;
113+
beneficiary_acc.balance += balance; // Keep balance if acc is the beneficiary.
114+
115+
// Return "selfdestruct not registered".
116+
// In practice this affects only refunds before Cancun.
117+
return false;
118+
}
119+
120+
// Transfer may happen multiple times per single account as account's balance
121+
// can be increased with a call following previous selfdestruct.
122+
beneficiary_acc.balance += balance;
123+
acc.balance = 0; // Zero balance if acc is the beneficiary.
109124

110125
// Mark the destruction if not done already.
111126
return !std::exchange(acc.destructed, true);
@@ -184,6 +199,8 @@ evmc::Result Host::create(const evmc_message& msg) noexcept
184199
if (m_rev >= EVMC_SPURIOUS_DRAGON)
185200
new_acc.nonce = 1;
186201

202+
new_acc.just_created = true;
203+
187204
// Clear the new account storage, but keep the access status (from tx access list).
188205
// This is only needed for tests and cannot happen in real networks.
189206
for (auto& [_, v] : new_acc.storage) [[unlikely]]

test/state/state.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,14 @@ std::variant<TransactionReceipt, std::error_code> transition(State& state, const
254254
if (rev >= EVMC_SPURIOUS_DRAGON)
255255
delete_empty_accounts(state);
256256

257-
// Set accounts and their storage access status to cold in the end of transition process
257+
// Post-transaction clean-up.
258+
// - Set accounts and their storage access status to cold.
259+
// - Clear the "just created" account flag.
258260
for (auto& [addr, acc] : state.get_accounts())
259261
{
260262
acc.transient_storage.clear();
261263
acc.access_status = EVMC_ACCESS_COLD;
264+
acc.just_created = false;
262265
for (auto& [key, val] : acc.storage)
263266
{
264267
val.access_status = EVMC_ACCESS_COLD;

test/unittests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ target_sources(
5151
state_transition_block_test.cpp
5252
state_transition_create_test.cpp
5353
state_transition_eof_test.cpp
54+
state_transition_selfdestruct_test.cpp
5455
state_transition_trace_test.cpp
5556
state_transition_transient_storage_test.cpp
5657
state_transition_tx_test.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2023 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "../utils/bytecode.hpp"
6+
#include "state_transition.hpp"
7+
8+
using namespace evmc::literals;
9+
using namespace evmone::test;
10+
11+
TEST_F(state_transition, selfdestruct_shanghai)
12+
{
13+
rev = EVMC_SHANGHAI;
14+
tx.to = To;
15+
pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)});
16+
17+
expect.post[To].exists = false;
18+
expect.post[0xbe_address].balance = 0x4e;
19+
}
20+
21+
TEST_F(state_transition, selfdestruct_cancun)
22+
{
23+
rev = EVMC_CANCUN;
24+
tx.to = To;
25+
pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(0xbe_address)});
26+
27+
expect.post[To].balance = 0;
28+
expect.post[0xbe_address].balance = 0x4e;
29+
}
30+
31+
TEST_F(state_transition, selfdestruct_to_self_cancun)
32+
{
33+
rev = EVMC_CANCUN;
34+
tx.to = To;
35+
pre.insert(*tx.to, {.balance = 0x4e, .code = selfdestruct(To)});
36+
37+
expect.post[To].balance = 0x4e;
38+
}
39+
40+
TEST_F(state_transition, selfdestruct_same_tx_cancun)
41+
{
42+
rev = EVMC_CANCUN;
43+
tx.value = 0x4e;
44+
tx.data = selfdestruct(0xbe_address);
45+
pre.get(Sender).balance += 0x4e;
46+
47+
expect.post[0xbe_address].balance = 0x4e;
48+
}

0 commit comments

Comments
 (0)