Skip to content

Commit

Permalink
state: Implement EIP-6780 "SELFDESTRUCT"
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chfast committed Nov 24, 2023
1 parent 0174b53 commit d2ecff0
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
3 changes: 3 additions & 0 deletions test/state/account.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 22 additions & 5 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 calling SELFDESTRUCT was created"
acc.balance = 0;
beneficiary_acc.balance += balance;

// 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 (this may be the beneficiary).

// Mark the destruction if not done already.
return !std::exchange(acc.destructed, true);
Expand Down Expand Up @@ -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]]
Expand Down
5 changes: 4 additions & 1 deletion test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,14 @@ std::variant<TransactionReceipt, std::error_code> 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;
Expand Down

0 comments on commit d2ecff0

Please sign in to comment.