diff --git a/packages/testing/src/execution_testing/execution/transaction_post.py b/packages/testing/src/execution_testing/execution/transaction_post.py index 32a340f9ac..59c9924906 100644 --- a/packages/testing/src/execution_testing/execution/transaction_post.py +++ b/packages/testing/src/execution_testing/execution/transaction_post.py @@ -7,6 +7,7 @@ from execution_testing.base_types import Address, Alloc, Hash from execution_testing.forks import Fork +from execution_testing.logging import get_logger from execution_testing.rpc import ( EngineRPC, EthRPC, @@ -19,6 +20,8 @@ from .base import BaseExecute +logger = get_logger(__name__) + class TransactionPost(BaseExecute): """ @@ -86,8 +89,17 @@ def execute( eth_rpc.send_wait_transaction(transaction) all_tx_hashes.append(transaction.hash) else: - with pytest.raises(SendTransactionExceptionError): + logger.info( + f"Sending transaction expecting rejection " + f"(expected error: {transaction.error})..." + ) + with pytest.raises( + SendTransactionExceptionError + ) as exc_info: eth_rpc.send_transaction(transaction) + logger.info( + f"Transaction rejected as expected: {exc_info.value}" + ) else: eth_rpc.send_wait_transactions(signed_txs) all_tx_hashes.extend([tx.hash for tx in signed_txs]) diff --git a/tests/byzantium/eip198_modexp_precompile/helpers.py b/tests/byzantium/eip198_modexp_precompile/helpers.py index 4aeef7bec6..5274d00778 100644 --- a/tests/byzantium/eip198_modexp_precompile/helpers.py +++ b/tests/byzantium/eip198_modexp_precompile/helpers.py @@ -155,10 +155,18 @@ class ModExpOutput(TestParameterGroup): call_success (bool): The return_code from CALL, 0 indicates unsuccessful call (out-of-gas), 1 indicates call succeeded. - returned_data(str): The output returnData is the expected + returned_data (Bytes): The output returnData is the expected output of the call. """ call_success: bool = True returned_data: Bytes + + def __len__(self) -> int: + """Return the length of the returned data.""" + return len(self.returned_data) + + def __bytes__(self) -> bytes: + """Return the returned data as bytes.""" + return bytes(self.returned_data) diff --git a/tests/osaka/eip7823_modexp_upper_bounds/conftest.py b/tests/osaka/eip7823_modexp_upper_bounds/conftest.py index 314421a074..f9528d8e92 100644 --- a/tests/osaka/eip7823_modexp_upper_bounds/conftest.py +++ b/tests/osaka/eip7823_modexp_upper_bounds/conftest.py @@ -168,6 +168,7 @@ def tx( ) -> Transaction: """Transaction to measure gas consumption of the ModExp precompile.""" return Transaction( + ty=0x02, sender=pre.fund_eoa(), to=gas_measure_contract, data=bytes(modexp_input), diff --git a/tests/osaka/eip7823_modexp_upper_bounds/test_eip_mainnet.py b/tests/osaka/eip7823_modexp_upper_bounds/test_eip_mainnet.py new file mode 100644 index 0000000000..e84bb20b27 --- /dev/null +++ b/tests/osaka/eip7823_modexp_upper_bounds/test_eip_mainnet.py @@ -0,0 +1,96 @@ +""" +Mainnet marked execute checklist tests for +[EIP-7823: ModExp Upper Bound](https://eips.ethereum.org/EIPS/eip-7823). +""" + +from typing import Dict + +import pytest +from execution_testing import ( + Alloc, + StateTestFiller, + Transaction, +) + +from ...byzantium.eip198_modexp_precompile.helpers import ( + ModExpInput, + ModExpOutput, +) +from ..eip7883_modexp_gas_increase.spec import Spec +from .spec import ref_spec_7823 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7823.git_path +REFERENCE_SPEC_VERSION = ref_spec_7823.version + +pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] + + +@pytest.fixture +def call_succeeds(modexp_expected: ModExpOutput) -> bool: + """Override `call_succeeds` to use the parametrized ModExpOutput value.""" + return modexp_expected.call_success + + +@pytest.mark.parametrize( + "modexp_input,modexp_expected", + [ + pytest.param( + ModExpInput( + base=b"\x01" * Spec.MAX_LENGTH_BYTES, + exponent=b"\x00", + modulus=b"\x02", + ), + ModExpOutput( + call_success=True, + returned_data="0x01", + ), + id="base-boundary-1024-bytes", + ), + ], +) +def test_modexp_boundary( + state_test: StateTestFiller, + pre: Alloc, + tx: Transaction, + post: Dict, +) -> None: + """ + Mainnet test at the 1024-byte boundary. + + Tests that the ModExp precompile correctly handles input at the maximum + allowed length (1024 bytes) per EIP-7823. + """ + state_test(pre=pre, tx=tx, post=post) + + +@pytest.mark.parametrize( + "modexp_input,modexp_expected", + [ + pytest.param( + ModExpInput( + base=b"\x01" * (Spec.MAX_LENGTH_BYTES + 1), + exponent=b"\x00", + modulus=b"\x02", + ), + ModExpOutput( + call_success=False, + returned_data="", + ), + id="base-over-boundary-1025-bytes", + ), + ], +) +def test_modexp_over_boundary( + state_test: StateTestFiller, + pre: Alloc, + tx: Transaction, + post: Dict, +) -> None: + """ + Mainnet test exceeding the 1024-byte boundary. + + Tests that the ModExp precompile correctly rejects input exceeding the + maximum allowed length (1024 bytes) per EIP-7823. This proves the EIP + is correctly activated. + """ + state_test(pre=pre, tx=tx, post=post) diff --git a/tests/osaka/eip7825_transaction_gas_limit_cap/test_eip_mainnet.py b/tests/osaka/eip7825_transaction_gas_limit_cap/test_eip_mainnet.py new file mode 100644 index 0000000000..967db84939 --- /dev/null +++ b/tests/osaka/eip7825_transaction_gas_limit_cap/test_eip_mainnet.py @@ -0,0 +1,67 @@ +""" +Mainnet marked execute checklist tests for +[EIP-7825: Transaction Gas Limit Cap](https://eips.ethereum.org/EIPS/eip-7825). +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Op, + StateTestFiller, + Storage, + Transaction, + TransactionException, +) + +from .spec import Spec, ref_spec_7825 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7825.git_path +REFERENCE_SPEC_VERSION = ref_spec_7825.version + +pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] + + +def test_tx_gas_limit_cap_at_maximum( + state_test: StateTestFiller, + pre: Alloc, +) -> None: + """Test transaction at exactly the gas limit cap (2^24).""" + storage = Storage() + contract_address = pre.deploy_contract( + code=Op.SSTORE(storage.store_next(1), 1) + Op.STOP, + ) + + tx = Transaction( + ty=0x02, + to=contract_address, + sender=pre.fund_eoa(), + gas_limit=Spec.tx_gas_limit_cap, + ) + + post = { + contract_address: Account(storage=storage), + } + + state_test(pre=pre, post=post, tx=tx) + + +@pytest.mark.exception_test +def test_tx_gas_limit_cap_exceeded( + state_test: StateTestFiller, + pre: Alloc, +) -> None: + """Test transaction exceeding the gas limit cap (2^24 + 1).""" + contract_address = pre.deploy_contract( + code=Op.SSTORE(0, 1) + Op.STOP, + ) + + tx = Transaction( + ty=0x02, + to=contract_address, + sender=pre.fund_eoa(), + gas_limit=Spec.tx_gas_limit_cap + 1, + error=TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM, + ) + + state_test(pre=pre, post={}, tx=tx) diff --git a/tests/osaka/eip7883_modexp_gas_increase/conftest.py b/tests/osaka/eip7883_modexp_gas_increase/conftest.py index 319c234c23..0f1114eeab 100644 --- a/tests/osaka/eip7883_modexp_gas_increase/conftest.py +++ b/tests/osaka/eip7883_modexp_gas_increase/conftest.py @@ -15,7 +15,7 @@ Transaction, keccak256, ) -from execution_testing.forks import Osaka +from execution_testing.forks import London, Osaka from ...byzantium.eip198_modexp_precompile.helpers import ModExpInput from .spec import Spec, Spec7883 @@ -252,6 +252,7 @@ def precompile_gas_modifier() -> int: @pytest.fixture def tx( + fork: Fork, pre: Alloc, gas_measure_contract: Address, modexp_input: ModExpInput, @@ -259,6 +260,7 @@ def tx( ) -> Transaction: """Transaction to measure gas consumption of the ModExp precompile.""" return Transaction( + ty=0x02 if fork >= London else 0x00, sender=pre.fund_eoa(), to=gas_measure_contract, data=bytes(modexp_input), diff --git a/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py b/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py index 2f9b395669..3f80c12d2a 100644 --- a/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py +++ b/tests/osaka/eip7883_modexp_gas_increase/test_eip_mainnet.py @@ -1,7 +1,5 @@ """ -Mainnet marked tests for EIP-7883 ModExp gas cost increase. - -Tests for ModExp gas cost increase in +Mainnet marked execute checklist tests for [EIP-7883: ModExp Gas Cost Increase](https://eips.ethereum.org/EIPS/eip-7883). """ @@ -14,7 +12,10 @@ Transaction, ) -from ...byzantium.eip198_modexp_precompile.helpers import ModExpInput +from ...byzantium.eip198_modexp_precompile.helpers import ( + ModExpInput, + ModExpOutput, +) from .spec import Spec, ref_spec_7883 REFERENCE_SPEC_GIT_PATH = ref_spec_7883.git_path @@ -23,6 +24,12 @@ pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] +@pytest.fixture +def call_succeeds(modexp_expected: ModExpOutput) -> bool: + """Override `call_succeeds` to use the parametrized ModExpOutput value.""" + return modexp_expected.call_success + + @pytest.mark.parametrize( "modexp_input,modexp_expected", [ @@ -35,8 +42,10 @@ declared_exponent_length=1, declared_modulus_length=1, ), - # expected result: - bytes.fromhex("04"), + ModExpOutput( + call_success=True, + returned_data="0x04", + ), id="32-bytes-long-base", ), pytest.param( @@ -48,8 +57,10 @@ declared_exponent_length=1, declared_modulus_length=1, ), - # expected result: - bytes.fromhex("01"), + ModExpOutput( + call_success=True, + returned_data="0x01", + ), id="33-bytes-long-base", # higher cost than 32 bytes ), pytest.param( @@ -61,8 +72,10 @@ declared_exponent_length=1024, declared_modulus_length=1, ), - # expected result: - bytes.fromhex("02"), + ModExpOutput( + call_success=True, + returned_data="0x02", + ), id="1024-bytes-long-exp", ), pytest.param( @@ -74,9 +87,11 @@ declared_exponent_length=3, declared_modulus_length=64, ), - # expected result: - bytes.fromhex( - "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2" + ModExpOutput( + call_success=True, + returned_data=( + "0xc36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2" + ), ), id="nagydani-1-pow0x10001", ), @@ -89,9 +104,11 @@ declared_exponent_length=64, declared_modulus_length=32, ), - # expected result: - bytes.fromhex( - "0000000000000000000000000000000000000000000000000000000000000001" + ModExpOutput( + call_success=True, + returned_data=( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ), ), id="zero-exponent-64bytes", ), @@ -102,8 +119,6 @@ def test_modexp_different_base_lengths( pre: Alloc, tx: Transaction, post: Dict, - modexp_input: ModExpInput, - modexp_expected: ModExpInput, ) -> None: """Mainnet test for triggering gas cost increase.""" state_test(pre=pre, tx=tx, post=post) diff --git a/tests/osaka/eip7939_count_leading_zeros/test_eip_mainnet.py b/tests/osaka/eip7939_count_leading_zeros/test_eip_mainnet.py new file mode 100644 index 0000000000..517ff16be2 --- /dev/null +++ b/tests/osaka/eip7939_count_leading_zeros/test_eip_mainnet.py @@ -0,0 +1,57 @@ +""" +Mainnet marked execute checklist tests for +[EIP-7939: Count leading zeros (CLZ)](https://eips.ethereum.org/EIPS/eip-7939). +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Op, + StateTestFiller, + Transaction, +) + +from .spec import ref_spec_7939 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7939.git_path +REFERENCE_SPEC_VERSION = ref_spec_7939.version + +pytestmark = [pytest.mark.valid_at("Osaka"), pytest.mark.mainnet] + + +@pytest.mark.parametrize( + "clz_input,clz_expected", + [ + pytest.param( + 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + 8, + id="clz-8-leading-zeros", + ), + pytest.param(0, 256, id="clz-all-zeros"), + ], +) +def test_clz_mainnet( + state_test: StateTestFiller, + pre: Alloc, + clz_input: int, + clz_expected: int, +) -> None: + """ + Test CLZ opcode on mainnet. + """ + sender = pre.fund_eoa() + contract_address = pre.deploy_contract( + code=Op.SSTORE(0, Op.CLZ(clz_input)), + storage={"0x00": "0xdeadbeef"}, + ) + tx = Transaction( + ty=0x02, + to=contract_address, + sender=sender, + gas_limit=200_000, + ) + post = { + contract_address: Account(storage={"0x00": clz_expected}), + } + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/osaka/eip7951_p256verify_precompiles/conftest.py b/tests/osaka/eip7951_p256verify_precompiles/conftest.py index 5c8b28b6bb..f42b987adb 100644 --- a/tests/osaka/eip7951_p256verify_precompiles/conftest.py +++ b/tests/osaka/eip7951_p256verify_precompiles/conftest.py @@ -177,6 +177,7 @@ def tx( ) -> Transaction: """Transaction for the test.""" return Transaction( + ty=0x02, gas_limit=tx_gas_limit, data=input_data, to=call_contract_address, diff --git a/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py b/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py index 8b8d2fb7b1..4aaf2b4166 100644 --- a/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py +++ b/tests/osaka/eip7951_p256verify_precompiles/test_eip_mainnet.py @@ -1,14 +1,10 @@ """ -Mainnet marked tests for [EIP-7951: Precompile for secp256r1 Curve Support](https://eips.ethereum.org/EIPS/eip-7951). +Mainnet marked execute checklist tests for +[EIP-7951: Precompile for secp256r1 Curve Support](https://eips.ethereum.org/EIPS/eip-7951). """ import pytest -from execution_testing import ( - Alloc, - Environment, - StateTestFiller, - Transaction, -) +from execution_testing import Alloc, StateTestFiller, Transaction from .spec import H, R, S, Spec, X, Y, ref_spec_7951 @@ -25,7 +21,7 @@ "input_data", [ pytest.param( - H( # 'hello world' r1 signed with privkey 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 + H( # 'hello world' r1 signed with private key 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 0xB94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9 ) + R( @@ -49,7 +45,7 @@ def test_valid( state_test: StateTestFiller, pre: Alloc, post: dict, tx: Transaction ) -> None: """Positive mainnet test for the P256VERIFY precompile.""" - state_test(env=Environment(), pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) @pytest.mark.parametrize( @@ -59,7 +55,7 @@ def test_valid( "input_data", [ pytest.param( - H( # 'hello world' k1 signed (with eth prefix) with privkey 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 + H( # 'hello world' k1 signed (with eth prefix) with private key 0xd946578401d1980aba1fc85df2a1ddc0d2d618aadd37b213f7f7f91a553b1499 # noqa: E501 0xD9EBA16ED0ECAE432B71FE008C98CC872BB4CC214D3220A36F365326CF807D68 ) + R( @@ -88,4 +84,4 @@ def test_invalid( The signature actually is a valid secp256k1 signature, so this is an interesting test case. """ - state_test(env=Environment(), pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx)