diff --git a/.github/configs/feature.yaml b/.github/configs/feature.yaml index 7fca18fd9ef..7849a5e116a 100644 --- a/.github/configs/feature.yaml +++ b/.github/configs/feature.yaml @@ -11,3 +11,7 @@ monad_runloop: evm-type: eels # Like `monad`, but `--monad-runloop` and eestnet chain id `30143` fill-params: -m blockchain_test --from=MONAD_EIGHT --until=MONAD_TEN --chain-id=30143 --monad-runloop -k "not invalid_header" + +monad_eip7981: + evm-type: eels + fill-params: -m blockchain_test --fork=MONAD_NEXT --chain-id=143 -k "not invalid_header" diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py index 6075481806c..29ef4dc86e3 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py @@ -1265,7 +1265,9 @@ def _process_with_marker_args( "Missing fork argument with 'valid_at_transition_to' marker." ) - if len(forks) > 1: + # A single EIP argument expands to one fork per enabling fork, so + # the limit is on the arguments rather than on the resolved forks. + if len(fork_args) > 1: raise Exception( "Too many forks specified to 'valid_at_transition_to' marker." ) diff --git a/packages/testing/src/execution_testing/forks/base_fork.py b/packages/testing/src/execution_testing/forks/base_fork.py index f842878c42f..38dea7ca65c 100644 --- a/packages/testing/src/execution_testing/forks/base_fork.py +++ b/packages/testing/src/execution_testing/forks/base_fork.py @@ -333,12 +333,25 @@ def _maybe_transitioned(fork_cls: "BaseForkMeta") -> "BaseForkMeta": @staticmethod def _is_subclass_of(a: "BaseForkMeta", b: "BaseForkMeta") -> bool: """ - Check if `a` is a subclass of `b`, taking fork transitions into - account. + Check if `a` is a subclass of `b`, taking fork transitions and + declared succession into account. + + A fork can follow another fork it does not inherit from, which + places it after that fork (and after everything that fork comes + after) in the fork order without adopting its behavior. """ a = BaseForkMeta._maybe_transitioned(a) b = BaseForkMeta._maybe_transitioned(b) - return issubclass(a, b) + if issubclass(a, b): + return True + # The metaclass sees its instances as plain classes, so the + # trait is reached through a cast, as elsewhere in this class. + followed = cast(Type["BaseFork"], a).follows() + while followed is not None: + if issubclass(followed, b): + return True + followed = followed.follows() + return False def __gt__(cls, other: "BaseForkMeta") -> bool: """Compare if a fork is newer than some other fork (cls > other).""" @@ -1407,6 +1420,17 @@ def enabling_forks(cls) -> Set[Type["BaseFork"]]: raise Exception(f"Class {cls.__name__} is not an EIP.") return cls._enabling_forks + @classmethod + def follows(cls) -> Type["BaseFork"] | None: + """ + Return the fork this one comes after without inheriting it. + + A fork that reuses another lineage's ordering overrides this; + comparisons then place it after that fork, and after everything + that fork comes after, while its behavior stays its own. + """ + return None + @classmethod def parent(cls) -> Type["BaseFork"] | None: """Return the parent fork.""" diff --git a/packages/testing/src/execution_testing/forks/forks/forks.py b/packages/testing/src/execution_testing/forks/forks/forks.py index ecf3b02f3ae..a4a8218052b 100644 --- a/packages/testing/src/execution_testing/forks/forks/forks.py +++ b/packages/testing/src/execution_testing/forks/forks/forks.py @@ -1805,12 +1805,6 @@ def _calculate_sstore_gas_mip8( return gas_cost -class MONAD_NEXT(MONAD_TEN): # noqa: N801 - """MONAD_NEXT fork, a placeholder identical to MONAD_TEN.""" - - pass - - class BPO1( Osaka, bpo_fork=True, @@ -1904,3 +1898,24 @@ def engine_payload_attribute_target_gas_limit(cls) -> bool: limit. """ return True + + +class MONAD_NEXT( # noqa: N801 + eips.EIP7981, + MONAD_TEN, +): + """ + MONAD_NEXT fork. + + Amsterdam-based successor to MONAD_TEN, adopting the EIP-7981 + changes. The Amsterdam changes it does not adopt stay out of the + fork by not being inherited at all; the fork order still places + MONAD_NEXT after Amsterdam through `follows`. + """ + + @classmethod + def follows(cls) -> type[BaseFork] | None: + """MONAD_NEXT comes after Amsterdam without inheriting it.""" + return Amsterdam + + pass diff --git a/src/ethereum/forks/monad_next/transactions.py b/src/ethereum/forks/monad_next/transactions.py index 24bb4880a74..71bce9409f3 100644 --- a/src/ethereum/forks/monad_next/transactions.py +++ b/src/ethereum/forks/monad_next/transactions.py @@ -30,6 +30,22 @@ TX_MAX_GAS_LIMIT = Uint(30_000_000) +ACCESS_LIST_ADDRESS_FLOOR_TOKENS = Uint(80) +""" +Floor data tokens contributed by a single access list address per +[EIP-7981]. + +[EIP-7981]: https://eips.ethereum.org/EIPS/eip-7981 +""" + +ACCESS_LIST_STORAGE_KEY_FLOOR_TOKENS = Uint(128) +""" +Floor data tokens contributed by a single access list storage key per +[EIP-7981]. + +[EIP-7981]: https://eips.ethereum.org/EIPS/eip-7981 +""" + @final @slotted_freezable @@ -587,10 +603,6 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]: num_non_zeros = ulen(tx.data) - num_zeros tokens_in_calldata = num_zeros + num_non_zeros * Uint(4) - # EIP-7623 floor price (note: no EVM costs) - calldata_floor_gas_cost = ( - tokens_in_calldata * GasCosts.TX_DATA_TOKEN_FLOOR + GasCosts.TX_BASE - ) data_cost = tokens_in_calldata * GasCosts.TX_DATA_TOKEN_STANDARD @@ -600,12 +612,28 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]: create_cost = Uint(0) access_list_cost = Uint(0) + tokens_in_access_list = Uint(0) if has_access_list(tx): for access in tx.access_list: access_list_cost += GasCosts.TX_ACCESS_LIST_ADDRESS access_list_cost += ( ulen(access.slots) * GasCosts.TX_ACCESS_LIST_STORAGE_KEY ) + tokens_in_access_list += ACCESS_LIST_ADDRESS_FLOOR_TOKENS + tokens_in_access_list += ( + ulen(access.slots) * ACCESS_LIST_STORAGE_KEY_FLOOR_TOKENS + ) + + # Data token floor cost for access list bytes. + access_list_cost += tokens_in_access_list * GasCosts.TX_DATA_TOKEN_FLOOR + + # Total floor tokens. + total_floor_tokens = tokens_in_calldata + tokens_in_access_list + + # EIP-7623 floor price (note: no EVM costs) + calldata_floor_gas_cost = ( + total_floor_tokens * GasCosts.TX_DATA_TOKEN_FLOOR + GasCosts.TX_BASE + ) auth_cost = Uint(0) if isinstance(tx, SetCodeTransaction): diff --git a/tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/conftest.py b/tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/conftest.py new file mode 100644 index 00000000000..de52075d396 --- /dev/null +++ b/tests/amsterdam/eip2780_reduce_intrinsic_tx_gas/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-2780 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/conftest.py b/tests/amsterdam/eip7708_eth_transfer_logs/conftest.py new file mode 100644 index 00000000000..fb1d9465b3e --- /dev/null +++ b/tests/amsterdam/eip7708_eth_transfer_logs/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-7708 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7778_block_gas_accounting_without_refunds/conftest.py b/tests/amsterdam/eip7778_block_gas_accounting_without_refunds/conftest.py new file mode 100644 index 00000000000..5cbdebdc9ef --- /dev/null +++ b/tests/amsterdam/eip7778_block_gas_accounting_without_refunds/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-7778 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7843_slotnum/conftest.py b/tests/amsterdam/eip7843_slotnum/conftest.py new file mode 100644 index 00000000000..6e43f18fb35 --- /dev/null +++ b/tests/amsterdam/eip7843_slotnum/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-7843 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/conftest.py b/tests/amsterdam/eip7928_block_level_access_lists/conftest.py new file mode 100644 index 00000000000..3bedcfe2651 --- /dev/null +++ b/tests/amsterdam/eip7928_block_level_access_lists/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-7928 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7976_increase_calldata_floor_cost/conftest.py b/tests/amsterdam/eip7976_increase_calldata_floor_cost/conftest.py index f92a0e003a0..9a252257297 100644 --- a/tests/amsterdam/eip7976_increase_calldata_floor_cost/conftest.py +++ b/tests/amsterdam/eip7976_increase_calldata_floor_cost/conftest.py @@ -370,3 +370,10 @@ def tx( blob_versioned_hashes=blob_versioned_hashes, error=tx_error, ) + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip7981_increase_access_list_cost/test_access_list_cost.py b/tests/amsterdam/eip7981_increase_access_list_cost/test_access_list_cost.py index 2d093e6d0bb..981617c0ec0 100644 --- a/tests/amsterdam/eip7981_increase_access_list_cost/test_access_list_cost.py +++ b/tests/amsterdam/eip7981_increase_access_list_cost/test_access_list_cost.py @@ -124,10 +124,11 @@ def test_access_list_token_calculation( expected_floor_cost = ( expected_floor_tokens * gas_costs.TX_DATA_TOKEN_FLOOR + gas_costs.TX_BASE + ) + if fork.is_eip_enabled(2780): # EIP-2780 anchors the floor on the decomposed intrinsic base; the # tx targets a non-self account, adding the recipient-access charge. - + gas_costs.COLD_ACCOUNT_ACCESS - ) + expected_floor_cost += gas_costs.COLD_ACCOUNT_ACCESS actual_floor_cost = fork.transaction_data_floor_cost_calculator()( data=b"", access_list=access_list ) diff --git a/tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py b/tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py index 62a899b346a..92b02d7def7 100644 --- a/tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py +++ b/tests/amsterdam/eip7981_increase_access_list_cost/test_fork_transition.py @@ -7,12 +7,13 @@ transition timestamp) and pin the per-transaction gas paid on each side, plus the validity flip for gas limits inside the uplift gap. -The post-fork intrinsic composes three repricings; the hand-derived +The post-fork intrinsic composes up to three repricings; the hand-derived expectations below keep each term explicit so the EIP-7981 surcharge is individually visible: -- EIP-2780 decomposes the flat pre-fork `TX_BASE` into the lowered base - plus the `COLD_ACCOUNT_ACCESS` recipient charge. +- EIP-2780, on the forks that enable it, decomposes the flat pre-fork + `TX_BASE` into the lowered base plus the `COLD_ACCOUNT_ACCESS` + recipient charge. - EIP-8038 reprices the per-address and per-storage-key access list charges to the fork's cold access costs. - EIP-7981 adds four floor tokens per access list byte, charged at @@ -116,11 +117,12 @@ def test_access_list_intrinsic_across_amsterdam_transition( ) expected_post = ( post_costs.TX_BASE - + post_costs.COLD_ACCOUNT_ACCESS + addresses * post_costs.TX_ACCESS_LIST_ADDRESS + total_keys * post_costs.TX_ACCESS_LIST_STORAGE_KEY + surcharge ) + if post_fork.is_eip_enabled(2780): + expected_post += post_costs.COLD_ACCOUNT_ACCESS timestamps = [PRE_FORK_TIMESTAMP, POST_FORK_TIMESTAMP] expected_intrinsics = [expected_pre, expected_post] @@ -322,10 +324,10 @@ def test_access_list_floor_across_amsterdam_transition( post_costs.TX_DATA_TOKEN_STANDARD ) + calculate_access_list_floor_tokens(access_list) expected_post = int( - post_costs.TX_BASE - + post_costs.COLD_ACCOUNT_ACCESS - + post_tokens * post_costs.TX_DATA_TOKEN_FLOOR + post_costs.TX_BASE + post_tokens * post_costs.TX_DATA_TOKEN_FLOOR ) + if post_fork.is_eip_enabled(2780): + expected_post += int(post_costs.COLD_ACCOUNT_ACCESS) timestamps = [PRE_FORK_TIMESTAMP, POST_FORK_TIMESTAMP] expected_floors = [expected_pre, expected_post] diff --git a/tests/amsterdam/eip7997_deterministic_factory_predeploy/conftest.py b/tests/amsterdam/eip7997_deterministic_factory_predeploy/conftest.py new file mode 100644 index 00000000000..de93abfcfef --- /dev/null +++ b/tests/amsterdam/eip7997_deterministic_factory_predeploy/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-7997 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8024_dupn_swapn_exchange/conftest.py b/tests/amsterdam/eip8024_dupn_swapn_exchange/conftest.py new file mode 100644 index 00000000000..d8c781bb489 --- /dev/null +++ b/tests/amsterdam/eip8024_dupn_swapn_exchange/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-8024 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/conftest.py b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/conftest.py new file mode 100644 index 00000000000..2d16c275db4 --- /dev/null +++ b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-8037 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8038_state_access_gas_cost_increase/conftest.py b/tests/amsterdam/eip8038_state_access_gas_cost_increase/conftest.py new file mode 100644 index 00000000000..cd29eb50dcb --- /dev/null +++ b/tests/amsterdam/eip8038_state_access_gas_cost_increase/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-8038 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8070_sparse_blobpool/conftest.py b/tests/amsterdam/eip8070_sparse_blobpool/conftest.py index cb2a757494c..dc3ee7c3ecd 100644 --- a/tests/amsterdam/eip8070_sparse_blobpool/conftest.py +++ b/tests/amsterdam/eip8070_sparse_blobpool/conftest.py @@ -148,3 +148,10 @@ def txs( ) txs.append(network_wrapped_tx) return txs + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8246_selfdestruct_no_burn/conftest.py b/tests/amsterdam/eip8246_selfdestruct_no_burn/conftest.py new file mode 100644 index 00000000000..4fb53dba402 --- /dev/null +++ b/tests/amsterdam/eip8246_selfdestruct_no_burn/conftest.py @@ -0,0 +1,10 @@ +"""Pytest (plugin) definitions local to EIP-8246 tests.""" + +import pytest + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + ) diff --git a/tests/amsterdam/eip8282_builder_execution_requests/conftest.py b/tests/amsterdam/eip8282_builder_execution_requests/conftest.py index e17cee467fc..7a42ba76785 100644 --- a/tests/amsterdam/eip8282_builder_execution_requests/conftest.py +++ b/tests/amsterdam/eip8282_builder_execution_requests/conftest.py @@ -1,8 +1,17 @@ """Fixtures for the EIP-8282 builder execution request tests.""" +import pytest + from ...common.system_contract_request_fixtures import ( blocks, # noqa: F401 included_requests, # noqa: F401 system_contract_interactions_per_block_copy, # noqa: F401 timestamp, # noqa: F401 ) + + +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Mark all tests in this subdir as not valid for Monad forks.""" + metafunc.definition.add_marker( + pytest.mark.not_valid_for("MONAD_EIGHT", subsequent_forks=True) + )