Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions test/state/host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,13 @@ bool Host::selfdestruct(const address& addr, const address& beneficiary) noexcep
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.
if (m_rev < EVMC_AMSTERDAM || beneficiary != addr)
{
// 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 (before EIP-8246)
}

// Mark the destruction if not done already.
if (!acc.destructed)
Expand Down
13 changes: 10 additions & 3 deletions test/state/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,16 @@ StateDiff State::build_diff(evmc_revision rev) const
{
if (m.destructed)
{
// TODO: This must be done even for just_created
// because destructed may pre-date just_created. Add test to evmone (EEST has it).
diff.deleted_accounts.emplace_back(addr);
if (rev >= EVMC_AMSTERDAM && m.balance != 0)
{
// Preserve the balance of the self-destructed account, no burn (EIP-8246).
diff.modified_accounts.emplace_back(StateDiff::Entry{addr, 0, m.balance});
}
Comment on lines +233 to +237
else
{
// Delete account. This must be done also for pre-funded just_created account.
diff.deleted_accounts.emplace_back(addr);
}
continue;
}
if (m.erase_if_empty && rev >= EVMC_SPURIOUS_DRAGON && m.is_empty())
Expand Down
87 changes: 87 additions & 0 deletions test/unittests/state_transition_selfdestruct_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,94 @@ TEST_F(state_transition, selfdestruct_double_revert)

TEST_F(state_transition, selfdestruct_initcode)
{
rev = EVMC_SHANGHAI;
tx.data = selfdestruct(0xbe_address);

expect.post[compute_create_address(tx.sender, tx.nonce)].exists = false;
expect.post[0xbe_address].exists = false;
}

TEST_F(state_transition, selfdestruct_initcode_amsterdam)
{
// A same-tx-created account that self-destructs ending with a zero balance must not be in the
// final state (EIP-8246). In this test we use initcode.
rev = EVMC_AMSTERDAM;
tx.data = selfdestruct(0xbe_address);

expect.post[compute_create_address(tx.sender, tx.nonce)].exists = false;
expect.post[0xbe_address].exists = false;
}

TEST_F(state_transition, selfdestruct_prefunded)
{
// Although burn is removed in EIP-8246, the deletion of a pre-funded account still happens.
rev = EVMC_CANCUN;
const auto created = compute_create_address(tx.sender, tx.nonce);
pre[created] = {.balance = 1};
tx.data = selfdestruct(0xbe_address); // Transfer to distinct beneficiary.

expect.post[created].exists = false; // Removed, despite pre-existing in the state.
expect.post[0xbe_address].balance = 1; // Pre-funded balance delivered to the beneficiary.
}

TEST_F(state_transition, selfdestruct_prefunded_amsterdam)
{
// Although burn is removed in EIP-8246, the deletion of a pre-funded account still happens.
rev = EVMC_AMSTERDAM;
const auto created = compute_create_address(tx.sender, tx.nonce);
pre[created] = {.balance = 1};
tx.data = selfdestruct(0xbe_address); // Transfer to distinct beneficiary.

expect.post[created].exists = false; // Removed, despite pre-existing in the state.
expect.post[0xbe_address].balance = 1; // Pre-funded balance delivered to the beneficiary.
}

TEST_F(state_transition, selfdestruct_prefunded_burn)
{
// Burn pre-funded ETH by self-destruct to self.
rev = EVMC_CANCUN;
const auto created = compute_create_address(tx.sender, tx.nonce);
pre[created] = {.balance = 1};
tx.data = selfdestruct(created);

expect.post[created].exists = false; // Removed, despite pre-existing in the state.
}

TEST_F(state_transition, selfdestruct_prefunded_burn_amsterdam)
{
// Burn is removed with EIP-8246, the balance must be preserved.
rev = EVMC_AMSTERDAM;
const auto created = compute_create_address(tx.sender, tx.nonce);
pre[created] = {.balance = 1};
tx.data = selfdestruct(created);

expect.post[created].balance = 1; // Balance preserved.
expect.post[created].nonce = 0;
expect.post[created].code = {};
}

TEST_F(state_transition, selfdestruct_sibling_create_then_destruct_amsterdam)
{
// A contract created in one sub-call and self-destructed in a sibling sub-call of the same
// transaction must still be removed (especially its code).
rev = EVMC_AMSTERDAM;
static constexpr auto F = 0xfac0_address; // Factory address.

const auto runtime = selfdestruct(0xbe_address);
const auto initcode = mstore(0, push(runtime)) + ret(32 - runtime.size(), runtime.size());
const auto created = compute_create_address(F, 1);

pre[F] = {.nonce = 1,
.balance = 1,
.code = mstore(0, push(initcode)) +
create().input(32 - initcode.size(), initcode.size()).value(1)};
pre[To] = {.code = call(F).gas(0xffffff) + call(created).gas(0xffffff)};
tx.to = To;

expect.post[created].exists = false; // Created and destructed in the same tx -> removed.
expect.post[0xbe_address].balance = 1; // Funds delivered to the beneficiary.
expect.post[F] = {.nonce = 2, .balance = 0}; // F created one contract and sent it the balance.
expect.post[To] = {};
}

TEST_F(state_transition, massdestruct_shanghai)
Expand Down