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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ F = TypeVar("F", bound=Callable[..., Any])
class _CallableChecklistItem:
"""Base type for all callable checklist items."""

# Items with sub-items are emitted as classes, so a call on them is a
# constructor call to mypy: accept the same arguments as `__call__`.
def __new__(cls, *args: Any, eip: Any = ..., **kwargs: Any) -> Any: ...
@overload
def __call__(self, func: F) -> F: ...
@overload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def generate_checklist_stubs(output: str | None, dry_run: bool) -> None:
"""
try:
# Add src to path so we can import the module
src_path = Path(__file__).parent.parent
src_path = Path(__file__).parents[2]
sys.path.insert(0, str(src_path))

from execution_testing.checklists.eip_checklist import (
Expand All @@ -129,6 +129,9 @@ def generate_checklist_stubs(output: str | None, dry_run: bool) -> None:
class _CallableChecklistItem:
"""Base type for all callable checklist items."""

# Items with sub-items are emitted as classes, so a call on them is a
# constructor call to mypy: accept the same arguments as `__call__`.
def __new__(cls, *args: Any, eip: Any = ..., **kwargs: Any) -> Any: ...
@overload
def __call__(self, func: F) -> F: ...
@overload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
general/code_coverage/eels = The EIP-8246 code paths (selfdestruct() in vm/instructions/system.py, clear_account_preserving_balance() in state_tracker.py, the accounts_to_delete loop in fork.py) are fully exercised by tests/amsterdam/eip8246_selfdestruct_no_burn together with the fork-aware same-tx tests in tests/cancun/eip6780_selfdestruct; verify with `fill <those paths> --fork Amsterdam --cov=ethereum.forks.amsterdam`
general/code_coverage/test_coverage = Run `fill <those paths> --cov=<test modules>`; the only test-module lines never executed are the `raise ValueError` guards for unhandled parametrize values
general/code_coverage/missed_lines = Missed spec lines are outside EIP-8246: fork.py header and PoW validation plus system-call plumbing, system.py CALL-family and CREATE failure branches, state_tracker.py journal edge cases, interpreter.py precompile and exception paths
opcode/test/execution_context/set_code = A set-code delegated EOA is never a same-transaction created account, so EIP-8246 leaves it unchanged; covered by tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_self_destruct and ::test_set_code_to_self_destructing_account_deployed_in_same_tx
opcode/test/return_data/buffer/parent = Return-data behavior of SELFDESTRUCT is unchanged; covered by tests/byzantium/eip211_return_data/test_selfdestruct.py::test_selfdestruct_clears_return_data
opcode/test/terminating/rollback/logs = The EIP-7708 transfer log emitted by a reverted SELFDESTRUCT is discarded with its frame; covered by tests/cancun/eip6780_selfdestruct/test_journal_revert.py::test_selfdestruct_balance_transfer_reverted (expects an empty receipt log list under EIP-7708)
opcode/test/gas_usage/normal = SELFDESTRUCT gas is unchanged by EIP-8246; measured with the EIP-8246 remnant in tests/amsterdam/eip8038_state_access_gas_cost_increase/test_selfdestruct_gas.py::test_same_tx_created_selfdestruct_self_burn and tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_selfdestruct.py::test_selfdestruct_to_self_in_create_tx
opcode/test/gas_usage/out_of_gas_execution = Same-transaction SELFDESTRUCT running out of gas keeps the contract's code and balance; covered by tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py::test_selfdestruct_to_self[same_tx-exact_gas_minus_1] and tests/amsterdam/eip8038_state_access_gas_cost_increase/test_selfdestruct_gas.py::test_selfdestruct_oog_boundary
opcode/test/gas_usage/order_of_operations/exact = tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py::test_selfdestruct_to_self[same_tx-exact_gas] asserts the EIP-8246 remnant when the frame has exactly the SELFDESTRUCT cost
opcode/test/gas_usage/order_of_operations/oog = tests/tangerine_whistle/eip150_operation_gas_costs/test_eip150_selfdestruct.py::test_selfdestruct_to_self[same_tx-exact_gas_minus_1] asserts no deletion mark when the frame is one gas short
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
general/code_coverage/second_client = Optional
opcode/test/mem_exp = SELFDESTRUCT takes no memory operands; EIP-8246 changes the finalization of an existing opcode, not its inputs
opcode/test/stack_overflow = Stack semantics of the pre-existing SELFDESTRUCT opcode are unchanged by EIP-8246
opcode/test/stack_underflow = Stack semantics of the pre-existing SELFDESTRUCT opcode are unchanged by EIP-8246
opcode/test/stack_complex_operations = Stack semantics of the pre-existing SELFDESTRUCT opcode are unchanged by EIP-8246
opcode/test/execution_context/tx_context = SELFDESTRUCT does not depend on transaction context
opcode/test/execution_context/block_context = SELFDESTRUCT does not depend on block context
opcode/test/gas_usage/memory_expansion = SELFDESTRUCT does not expand memory
opcode/test/gas_usage/out_of_gas_memory = SELFDESTRUCT does not expand memory
opcode/test/out_of_bounds = SELFDESTRUCT's only operand is an address; values above 2**160-1 are masked to 20 bytes, a pre-existing rule that EIP-8246 does not touch
opcode/test/return_data/buffer/current = SELFDESTRUCT halts its frame, so there is no current-context return buffer to observe after it
opcode/test/data_portion = SELFDESTRUCT has no data portion
opcode/test/contract_creation = SELFDESTRUCT does not create contracts; the CREATE2 redeploy over a cleared remnant is covered under initcode re-entry
opcode/test/terminating/rollback/storage = SELFDESTRUCT writes no storage; its revertable effects are the balance move, the transfer log and the deletion mark
opcode/test/terminating/rollback/nonce = SELFDESTRUCT increments no nonce; its revertable effects are the balance move, the transfer log and the deletion mark
opcode/test/terminating/rollback/contracts = SELFDESTRUCT creates no contract; its revertable effects are the balance move, the transfer log and the deletion mark
opcode/test/fork_transition/invalid = SELFDESTRUCT is valid before and after Amsterdam; only its finalization changes
precompile = EIP does not introduce a new precompile
removed_precompile = EIP does not remove a precompile
system_contract = EIP does not introduce a new system contract
transaction_type = EIP does not introduce a new transaction type
block_header_field = EIP does not add any new block header fields
block_body_field = EIP does not add any new block body fields
gas_cost_changes = EIP does not change any gas cost; SELFDESTRUCT gas in Amsterdam is owned by EIP-8037 and EIP-8038
gas_refunds_changes = EIP does not change any gas refund
blob_count_changes = EIP does not introduce any blob count changes
execution_layer_request = EIP does not introduce an execution layer request
new_transaction_validity_constraint = EIP does not introduce a new transaction validity constraint
modified_transaction_validity_constraint = EIP does not modify a transaction validity constraint
block_level_constraint = EIP does not introduce a block-level validation constraint
2 changes: 1 addition & 1 deletion tests/amsterdam/eip8246_selfdestruct_no_burn/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class ReferenceSpec:

ref_spec_8246 = ReferenceSpec(
git_path="EIPS/eip-8246.md",
version="3b30ff829e5e698f1c6f69427111d194b80af38d",
version="8be64cf6a01350938b93332cf0062ab7a3166f23",
)
83 changes: 83 additions & 0 deletions tests/amsterdam/eip8246_selfdestruct_no_burn/test_eip_mainnet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
Mainnet marked execute checklist tests for
[EIP-8246: Remove SELFDESTRUCT Burn](https://eips.ethereum.org/EIPS/eip-8246).
"""

import pytest
from execution_testing import (
Account,
Alloc,
Op,
StateTestFiller,
Transaction,
compute_create_address,
)
from execution_testing import (
Macros as Om,
)

from .spec import ref_spec_8246

REFERENCE_SPEC_GIT_PATH = ref_spec_8246.git_path
REFERENCE_SPEC_VERSION = ref_spec_8246.version

pytestmark = [pytest.mark.valid_at("EIP8246"), pytest.mark.mainnet]

ENDOWMENT = 1


def test_create_tx_selfdestruct_to_self_keeps_balance(
state_test: StateTestFiller,
pre: Alloc,
) -> None:
"""A creation transaction whose initcode self-destructs to itself."""
sender = pre.fund_eoa()
created = compute_create_address(address=sender, nonce=sender.nonce)

tx = Transaction(
sender=sender,
to=None,
value=ENDOWMENT,
data=Op.SELFDESTRUCT(Op.ADDRESS),
)

state_test(
pre=pre,
post={
created: Account(balance=ENDOWMENT, nonce=0, code=b"", storage={})
},
tx=tx,
)


def test_factory_create_selfdestruct_to_self_keeps_balance(
state_test: StateTestFiller,
pre: Alloc,
) -> None:
"""
A factory CREATEs a contract whose initcode self-destructs to itself.
The factory then reads the new account's balance and finds the
endowment still there.
"""
sender = pre.fund_eoa()
initcode = Op.SELFDESTRUCT(Op.ADDRESS)
factory = pre.deploy_contract(
code=Om.MSTORE(initcode, 0)
+ Op.SSTORE(
0, Op.CREATE(value=Op.CALLVALUE, offset=0, size=len(initcode))
)
+ Op.SSTORE(1, Op.BALANCE(Op.SLOAD(0)))
+ Op.STOP
)
created = compute_create_address(address=factory, nonce=1)

tx = Transaction(sender=sender, to=factory, value=ENDOWMENT)

state_test(
pre=pre,
post={
factory: Account(nonce=2, storage={0: created, 1: ENDOWMENT}),
created: Account(balance=ENDOWMENT, nonce=0, code=b"", storage={}),
},
tx=tx,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""
Fork transition tests for [EIP-8246: Remove SELFDESTRUCT Burn](https://eips.ethereum.org/EIPS/eip-8246).
"""

import pytest
from execution_testing import (
Account,
Alloc,
Block,
BlockchainTestFiller,
Hash,
Op,
Transaction,
compute_create_address,
)
from execution_testing import (
Macros as Om,
)
from execution_testing.checklists import EIPChecklist

from .spec import ref_spec_8246

REFERENCE_SPEC_GIT_PATH = ref_spec_8246.git_path
REFERENCE_SPEC_VERSION = ref_spec_8246.version


@pytest.mark.valid_at_transition_to("EIP8246")
@EIPChecklist.Opcode.Test.ForkTransition.At()
def test_selfdestruct_to_self_fork_transition(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
) -> None:
"""
A contract created in the transaction self-destructs to itself. Before
the fork its balance is burned and the account is deleted; from the fork
block on the account stays behind holding that balance.
"""
endowment = 5
sender = pre.fund_eoa()
initcode = Op.SELFDESTRUCT(Op.ADDRESS)
factory = pre.deploy_contract(
code=Om.MSTORE(initcode, 0)
+ Op.SSTORE(
Op.CALLDATALOAD(0),
Op.CREATE(value=Op.CALLVALUE, offset=0, size=len(initcode)),
)
+ Op.STOP
)
before = compute_create_address(address=factory, nonce=1)
at = compute_create_address(address=factory, nonce=2)
after = compute_create_address(address=factory, nonce=3)

def create_tx(index: int) -> Transaction:
return Transaction(
sender=sender, to=factory, value=endowment, data=Hash(index)
)

blockchain_test(
pre=pre,
blocks=[
Block(timestamp=14_999, txs=[create_tx(1)]),
Block(timestamp=15_000, txs=[create_tx(2)]),
Block(timestamp=15_001, txs=[create_tx(3)]),
],
post={
factory: Account(nonce=4, storage={1: before, 2: at, 3: after}),
before: Account.NONEXISTENT,
at: Account(balance=endowment, nonce=0, code=b"", storage={}),
after: Account(balance=endowment, nonce=0, code=b"", storage={}),
},
)
Loading
Loading