Repository: ethereum/execution-spec-tests
Pull request #1441: feat(tests): add increased nonce with invalid nonce tests
Author: login: danceratopz
Labels: name: scope:tests
description: Scope: Changes EL client test cases in `./tests`
name: type:feat
description: type: Feature
name: fork:prague
description: Prague hardfork
Created: 2025-04-11T12:09:40Z | Merged: 2025-04-15T22:08:31Z

## 🗒️ Description

This PR attempts to add some missing test cases based purely on a coverage analysis of the test source itself. In particular, L269 in `test_gas` was not hit:
https://github.com/ethereum/execution-spec-tests/blob/62d2563dcfd58242f1846bab13f29a64dc558567/tests/prague/eip7702_set_code_tx/test_gas.py#L256-L276

Either of these two test cases fill the line coverage gap, but I am out of my depth with 7702 and would appreciate feedback on the test cases and whether they're reasonable. In particular, the second case fails to fill for all but one parameter combo - it need further restriction on the conditions it runs (ran out of time on this one).
1. The `multiple_invalid_nonce_authorizations_self_sponsored_multiple_signers` test cases fill and add coverage. All clients pass with consume-engine.
2. The `single_invalid_authorization_eoa_authority_multiple_signers` test cases only fill successfully for one parameter combo - this is WIP, needs further debugging.

Perhaps this already helps you understand the fails from 2.:
```
FAILED tests/prague/eip7702_set_code_tx/test_gas.py::test_gas_cost[fork_Prague-state_test-single_invalid_authorization_eoa_authority_multiple_signers] - ethereum_test_base_types.composite_types.Storage.KeyValueMismatchError: incorrect value in address 0x0000000000000000000000000000000000001000 (test_code_address) for key 0x0000000000000000000000000000000000000000000000000000000000000000: want 0x35f81 (dec:221057), got 0x0 (dec:0)
FAILED tests/prague/eip7702_set_code_tx/test_gas.py::test_account_warming[fork_Prague-state_test-single_invalid_authorization_eoa_authority_multiple_signers-check_delegated_account_first_True] - ethereum_test_base_types.composite_types.Storage.KeyValueMismatchError: incorrect value in address 0x0000000000000000000000000000000000001000 (callee_address) for key 0x0000000000000000000000000000000000000000000000000000000000000001: want 0xa28 (dec:2600), got 0xa8c (dec:2700)
FAILED tests/prague/eip7702_set_code_tx/test_gas.py::test_account_warming[fork_Prague-state_test-single_invalid_authorization_eoa_authority_multiple_signers-check_delegated_account_first_False] - ethereum_test_base_types.composite_types.Storage.KeyValueMismatchError: incorrect value in address 0x0000000000000000000000000000000000001000 (callee_address) for key 0x0000000000000000000000000000000000000000000000000000000000000000: want 0xa28 (dec:2600), got 0x1450 (dec:5200)
FAILED tests/prague/eip7702_set_code_tx/test_gas.py::test_intrinsic_gas_cost[fork_Prague-state_test-valid_True-single_invalid_authorization_eoa_authority_multiple_signers] - ethereum_test_specs.helpers.UnexpectedExecutionFailError: Unexpected fail for Transaction ({'index': 0, 'nonce': 0}):
```

Generate coverage for this module with:
```
uv sync --all-extras
fill --until Prague -m state_test
 tests/prague/eip7702_set_code_tx/test_gas.py  --cov tests --cov-report=html:7702-coverage
```

## 🔗 Related Issues
None

## ✅ Checklist

- [x] All: Set appropriate labels for the changes.
- [ ] All: Considered squashing commits to improve commit history.
- [x] All: Added an entry to [CHANGELOG.md](/ethereum/execution-spec-tests/blob/main/docs/CHANGELOG.md).




Diff:
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 9a3386ebdf2..e265c71c506 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -25,6 +25,7 @@ Test fixtures for use by clients are available for each release on the [Github r
 ### 🧪 Test Cases
 
 - ✨ [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Test precompile case in same transaction as delegation without extra gas in case of precompile code execution; parametrize all call opcodes in existing precompile test ([#1431](https://github.com/ethereum/execution-spec-tests/pull/1431)).
+- ✨ [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Add invalid nonce authorizations tests for the case of multiple signers when the sender's nonce gets increased ([#1441](https://github.com/ethereum/execution-spec-tests/pull/1441)).
 - ✨ [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623): Additionally parametrize transaction validity tests with the `to` set to an EOA account (previously only contracts) ([#1422](https://github.com/ethereum/execution-spec-tests/pull/1422)).
 
 ## [v4.2.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.2.0) - 2025-04-08
diff --git a/eels_resolutions.json b/eels_resolutions.json
index fd5d3ce6a30..1fd545fa6d7 100644
--- a/eels_resolutions.json
+++ b/eels_resolutions.json
@@ -35,7 +35,8 @@
         "same_as": "EELSMaster"
     },
     "Prague": {
-        "git_url": "https://github.com/ethereum/execution-specs.git",
-        "branch": "devnets/prague/6"
+      "git_url": "https://github.com/gurukamath/execution-specs.git",
+      "branch": "7702-to-precompile",
+      "commit": "bbc469729ab095300b07de8fe323c3f9fac31857"
     }
 }
diff --git a/src/ethereum_test_tools/code/generators.py b/src/ethereum_test_tools/code/generators.py
index f40eebc6082..2ac76ae9e5b 100644
--- a/src/ethereum_test_tools/code/generators.py
+++ b/src/ethereum_test_tools/code/generators.py
@@ -149,7 +149,7 @@ class CodeGasMeasure(Bytecode):
     To be considered when subtracting the value of the previous GAS operation,
     and to be popped at the end of the execution.
     """
-    sstore_key: int
+    sstore_key: int | Bytes
     """
     Storage key to save the gas used.
     """
@@ -160,7 +160,7 @@ def __new__(
         code: Bytecode,
         overhead_cost: int = 0,
         extra_stack_items: int = 0,
-        sstore_key: int = 0,
+        sstore_key: int | Bytes = 0,
         stop: bool = True,
     ):
         """Assemble the bytecode that measures gas usage."""
@@ -173,9 +173,7 @@ def __new__(
             + Op.SUB
             + Op.PUSH1(overhead_cost + 2)
             + Op.SWAP1
-            + Op.SUB
-            + Op.PUSH1(sstore_key)
-            + Op.SSTORE
+            + Op.SSTORE(sstore_key, Op.SUB)
         )
         if stop:
             res += Op.STOP
diff --git a/tests/prague/eip7702_set_code_tx/test_gas.py b/tests/prague/eip7702_set_code_tx/test_gas.py
index 47e51367ef9..66628a37f3a 100644
--- a/tests/prague/eip7702_set_code_tx/test_gas.py
+++ b/tests/prague/eip7702_set_code_tx/test_gas.py
@@ -92,9 +92,22 @@ class AuthorityWithProperties:
     """Dataclass to hold the properties of the authority address."""
 
     authority: EOA
+    """
+    The address of the authority to be used in the transaction.
+    """
     address_type: AddressType
+    """
+    The type of the address the authority was before the authorization.
+    """
     invalidity_type: AuthorizationInvalidityType | None
-    empty: bool
+    """
+    Whether the authorization will be invalid and if so, which type of invalidity it is.
+    """
+
+    @property
+    def empty(self) -> bool:
+        """Return True if the authority address is an empty account before the authorization."""
+        return self.address_type == AddressType.EMPTY_ACCOUNT
 
 
 @pytest.fixture()
@@ -125,7 +138,6 @@ def generator(
                         authority=pre.fund_eoa(0),
                         address_type=current_authority_type,
                         invalidity_type=None,
-                        empty=True,
                     )
                 case AddressType.EOA:
                     if i == 0 and self_sponsored:
@@ -133,14 +145,12 @@ def generator(
                             authority=sender,
                             address_type=current_authority_type,
                             invalidity_type=None,
-                            empty=False,
                         )
                     else:
                         yield AuthorityWithProperties(
                             authority=pre.fund_eoa(),
                             address_type=current_authority_type,
                             invalidity_type=None,
-                            empty=False,
                         )
                 case AddressType.EOA_WITH_SET_CODE:
                     if i == 0 and self_sponsored:
@@ -148,14 +158,12 @@ def generator(
                             authority=sender,
                             address_type=current_authority_type,
                             invalidity_type=None,
-                            empty=False,
                         )
                     else:
                         yield AuthorityWithProperties(
                             authority=pre.fund_eoa(0, delegation=authorize_to_address),
                             address_type=current_authority_type,
                             invalidity_type=None,
-                            empty=False,
                         )
                 case AddressType.CONTRACT:
                     assert not self_sponsored or i > 0, (
@@ -169,7 +177,6 @@ def generator(
                         authority=authority,
                         address_type=current_authority_type,
                         invalidity_type=AuthorizationInvalidityType.AUTHORITY_IS_CONTRACT,
-                        empty=False,
                     )
                 case _:
                     raise ValueError(f"Unsupported authority type: {current_authority_type}")
@@ -182,9 +189,29 @@ class AuthorizationWithProperties:
     """Dataclass to hold the properties of the authorization list."""
 
     tuple: AuthorizationTuple
+    """
+    The authorization tuple to be used in the transaction.
+    """
     invalidity_type: AuthorizationInvalidityType | None
-    empty: bool
+    """
+    Whether the authorization is invalid and if so, which type of invalidity it is.
+    """
+    authority_type: AddressType
+    """
+    The type of the address the authority was before the authorization.
+    """
     skip: bool
+    """
+    Whether the authorization should be skipped and therefore not included in the transaction.
+
+    Used for tests where the authorization was already in the state before the transaction was
+    created.
+    """
+
+    @property
+    def empty(self) -> bool:
+        """Return True if the authority address is an empty account before the authorization."""
+        return self.authority_type == AddressType.EMPTY_ACCOUNT
 
 
 @pytest.fixture
@@ -192,6 +219,7 @@ def authorization_list_with_properties(
     signer_type: SignerType,
     authorization_invalidity_type: AuthorizationInvalidityType | None,
     authorizations_count: int,
+    invalid_authorization_index: int,
     chain_id_type: ChainIDType,
     authority_iterator: Iterator[AuthorityWithProperties],
     authorize_to_address: Address,
@@ -199,10 +227,6 @@ def authorization_list_with_properties(
     re_authorize: bool,
 ) -> List[AuthorizationWithProperties]:
     """Fixture to return the authorization-list-with-properties for the given case."""
-    chain_id = 0 if chain_id_type == ChainIDType.GENERIC else 1
-    if authorization_invalidity_type == AuthorizationInvalidityType.INVALID_CHAIN_ID:
-        chain_id = 2
-
     authorization_list: List[AuthorizationWithProperties] = []
     match signer_type:
         case SignerType.SINGLE_SIGNER:
@@ -214,15 +238,6 @@ def authorization_list_with_properties(
                 or authority_with_properties.address_type == AddressType.EOA_WITH_SET_CODE
             )
             for i in range(authorizations_count):
-                # Get the nonce of this authorization
-                match authorization_invalidity_type:
-                    case AuthorizationInvalidityType.INVALID_NONCE:
-                        nonce = 0 if increased_nonce else 1
-                    case AuthorizationInvalidityType.REPEATED_NONCE:
-                        nonce = 1 if increased_nonce else 0
-                    case _:
-                        nonce = i if not increased_nonce else i + 1
-
                 # Get the validity of this authorization
                 invalidity_type: AuthorizationInvalidityType | None
                 if authorization_invalidity_type is None or (
@@ -231,13 +246,28 @@ def authorization_list_with_properties(
                 ):
                     invalidity_type = authority_with_properties.invalidity_type
                 else:
-                    invalidity_type = authorization_invalidity_type
-                skip = False
-                if (
+                    if i == invalid_authorization_index or invalid_authorization_index == -1:
+                        invalidity_type = authorization_invalidity_type
+                    else:
+                        invalidity_type = authority_with_properties.invalidity_type
+
+                # Get the nonce of this authorization
+                match invalidity_type:
+                    case AuthorizationInvalidityType.INVALID_NONCE:
+                        nonce = 0 if increased_nonce else 1
+                    case AuthorizationInvalidityType.REPEATED_NONCE:
+                        nonce = 1 if increased_nonce else 0
+                    case _:
+                        nonce = i if not increased_nonce else i + 1
+
+                chain_id = 0 if chain_id_type == ChainIDType.GENERIC else 1
+                if invalidity_type == AuthorizationInvalidityType.INVALID_CHAIN_ID:
+                    chain_id = 2
+
+                skip = (
                     authority_with_properties.address_type == AddressType.EOA_WITH_SET_CODE
                     and not re_authorize
-                ):
-                    skip = True
+                )
                 authorization_list.append(
                     AuthorizationWithProperties(
                         tuple=AuthorizationTuple(
@@ -247,7 +277,7 @@ def authorization_list_with_properties(
                             signer=authority_with_properties.authority,
                         ),
                         invalidity_type=invalidity_type,
-                        empty=authority_with_properties.empty,
+                        authority_type=authority_with_properties.address_type,
                         skip=skip,
                     )
                 )
@@ -259,30 +289,38 @@ def authorization_list_with_properties(
                 authority_iterator = cycle([next(authority_iterator), next(authority_iterator)])
 
             for i in range(authorizations_count):
-                # Get the nonce of this authorization
                 authority_with_properties = next(authority_iterator)
+                # Get the validity of this authorization
+                if authorization_invalidity_type is None or (
+                    authorization_invalidity_type == AuthorizationInvalidityType.REPEATED_NONCE
+                    and i <= 1
+                ):
+                    invalidity_type = authority_with_properties.invalidity_type
+                else:
+                    if i == invalid_authorization_index or invalid_authorization_index == -1:
+                        invalidity_type = authorization_invalidity_type
+                    else:
+                        invalidity_type = authority_with_properties.invalidity_type
+
+                # Get the nonce of this authorization
                 increased_nonce = (
                     self_sponsored and i == 0
                 ) or authority_with_properties.address_type == AddressType.EOA_WITH_SET_CODE
                 if increased_nonce:
-                    if authorization_invalidity_type == AuthorizationInvalidityType.INVALID_NONCE:
+                    if invalidity_type == AuthorizationInvalidityType.INVALID_NONCE:
                         nonce = 0
                     else:
                         nonce = 1
                 else:
-                    if authorization_invalidity_type == AuthorizationInvalidityType.INVALID_NONCE:
+                    if invalidity_type == AuthorizationInvalidityType.INVALID_NONCE:
                         nonce = 1
                     else:
                         nonce = 0
 
-                # Get the validity of this authorization
-                if authorization_invalidity_type is None or (
-                    authorization_invalidity_type == AuthorizationInvalidityType.REPEATED_NONCE
-                    and i <= 1
-                ):
-                    invalidity_type = authority_with_properties.invalidity_type
-                else:
-                    invalidity_type = authorization_invalidity_type
+                chain_id = 0 if chain_id_type == ChainIDType.GENERIC else 1
+                if invalidity_type == AuthorizationInvalidityType.INVALID_CHAIN_ID:
+                    chain_id = 2
+
                 skip = False
                 if (
                     authority_with_properties.address_type == AddressType.EOA_WITH_SET_CODE
@@ -298,7 +336,7 @@ def authorization_list_with_properties(
                             signer=authority_with_properties.authority,
                         ),
                         invalidity_type=invalidity_type,
-                        empty=authority_with_properties.empty,
+                        authority_type=authority_with_properties.address_type,
                         skip=skip,
                     )
                 )
@@ -387,6 +425,7 @@ def gas_test_parameter_args(
         "signer_type": SignerType.SINGLE_SIGNER,
         "authorization_invalidity_type": None,
         "authorizations_count": 1,
+        "invalid_authorization_index": -1,  # All authorizations are equally invalid
         "chain_id_type": ChainIDType.GENERIC,
         "authorize_to_address": AddressType.EMPTY_ACCOUNT,
         "access_list_case": AccessListType.EMPTY,
@@ -435,6 +474,28 @@ def gas_test_parameter_args(
             },
             id="single_invalid_authorization_invalid_chain_id_single_signer",
         ),
+        pytest.param(
+            {
+                "authority_type": AddressType.EOA_WITH_SET_CODE,
+                "signer_type": SignerType.MULTIPLE_SIGNERS,
+                "re_authorize": True,
+                "authorization_invalidity_type": AuthorizationInvalidityType.INVALID_NONCE,
+                "authorizations_count": multiple_authorizations_count,
+                "invalid_authorization_index": 0,
+            },
+            id="single_invalid_authorization_eoa_authority_multiple_signers_1",
+        ),
+        pytest.param(
+            {
+                "authority_type": AddressType.EOA_WITH_SET_CODE,
+                "signer_type": SignerType.MULTIPLE_SIGNERS,
+                "re_authorize": True,
+                "authorization_invalidity_type": AuthorizationInvalidityType.INVALID_NONCE,
+                "authorizations_count": multiple_authorizations_count,
+                "invalid_authorization_index": multiple_authorizations_count - 1,
+            },
+            id="single_invalid_authorization_eoa_authority_multiple_signers_2",
+        ),
         pytest.param(
             {
                 "signer_type": SignerType.SINGLE_SIGNER,
@@ -451,6 +512,16 @@ def gas_test_parameter_args(
             },
             id="multiple_invalid_nonce_authorizations_multiple_signers",
         ),
+        pytest.param(
+            {
+                "signer_type": SignerType.MULTIPLE_SIGNERS,
+                "authority_type": AddressType.EOA,
+                "authorization_invalidity_type": AuthorizationInvalidityType.INVALID_NONCE,
+                "self_sponsored": True,
+                "authorizations_count": multiple_authorizations_count,
+            },
+            id="multiple_invalid_nonce_authorizations_self_sponsored_multiple_signers",
+        ),
         pytest.param(
             {
                 "signer_type": SignerType.SINGLE_SIGNER,
@@ -755,7 +826,6 @@ def test_account_warming(
     pre: Alloc,
     authorization_list_with_properties: List[AuthorizationWithProperties],
     authorization_list: List[AuthorizationTuple],
-    access_list_case: AccessListType,
     access_list: List[AccessList],
     data: bytes,
     sender: EOA,
@@ -768,6 +838,8 @@ def test_account_warming(
     cold_account_cost = 2600
     warm_account_cost = 100
 
+    access_list_addresses = {access_list.address for access_list in access_list}
+
     # Dictionary to keep track of the addresses to check for warming, and the expected cost of
     # accessing such account.
     addresses_to_check: Dict[Address, int] = {}
@@ -777,11 +849,19 @@ def test_account_warming(
         assert authority is not None, "authority address is not set"
         delegated_account = authorization_with_properties.tuple.address
 
+        authority_contains_delegation_after_authorization = (
+            authorization_with_properties.invalidity_type is None
+            # If the authority already contained a delegation prior to the transaction,
+            # even if the authorization is invalid, there will be a delegation when we
+            # check the address.
+            or authorization_with_properties.authority_type == AddressType.EOA_WITH_SET_CODE
+        )
+
         if check_delegated_account_first:
             if delegated_account not in addresses_to_check:
                 addresses_to_check[delegated_account] = (
                     warm_account_cost
-                    if access_list_case.contains_set_code_address()
+                    if delegated_account in access_list_addresses
                     else cold_account_cost
                 )
 
@@ -793,21 +873,22 @@ def test_account_warming(
                             authorization_with_properties.invalidity_type
                             != AuthorizationInvalidityType.INVALID_CHAIN_ID
                         )
-                        or access_list_case.contains_authority()
+                        or authority in access_list_addresses
                     ):
                         access_cost = warm_account_cost
                     else:
                         access_cost = cold_account_cost
-                    if authorization_with_properties.invalidity_type is None:
-                        access_cost += warm_account_cost
                 else:
                     access_cost = (
                         cold_account_cost
                         if Address(sender) != authorization_with_properties.tuple.signer
                         else warm_account_cost
                     )
-                    if authorization_with_properties.invalidity_type is None:
-                        access_cost += warm_account_cost
+
+                if authority_contains_delegation_after_authorization:
+                    # The double charge for accessing the delegated account, only if the
+                    # account ends up with a delegation in its code.
+                    access_cost += warm_account_cost
 
                 addresses_to_check[authority] = access_cost
 
@@ -824,18 +905,18 @@ def test_account_warming(
                         authorization_with_properties.invalidity_type
                         != AuthorizationInvalidityType.INVALID_CHAIN_ID
                     )
-                    or access_list_case.contains_authority()
+                    or authority in access_list_addresses
                 ):
                     access_cost = warm_account_cost
 
                 if (
                     # We can only charge the delegated account access cost if the authorization
                     # went through
-                    authorization_with_properties.invalidity_type is None
+                    authority_contains_delegation_after_authorization
                 ):
                     if (
                         delegated_account in addresses_to_check
-                        or access_list_case.contains_set_code_address()
+                        or delegated_account in access_list_addresses
                     ):
                         access_cost += warm_account_cost
                     else:
@@ -845,29 +926,31 @@ def test_account_warming(
 
             if delegated_account not in addresses_to_check:
                 if (
-                    authorization_with_properties.invalidity_type is None
-                    or access_list_case.contains_set_code_address()
+                    authority_contains_delegation_after_authorization
+                    or delegated_account in access_list_addresses
                 ):
                     access_cost = warm_account_cost
                 else:
                     access_cost = cold_account_cost
                 addresses_to_check[delegated_account] = access_cost
 
-    callee_storage = Storage()
     callee_code: Bytecode = sum(  # type: ignore
         (
             CodeGasMeasure(
                 code=Op.CALL(gas=0, address=check_address),
                 overhead_cost=overhead_cost,
                 extra_stack_items=1,
-                sstore_key=callee_storage.store_next(access_cost),
+                sstore_key=check_address,
                 stop=False,
             )
-            for check_address, access_cost in addresses_to_check.items()
+            for check_address in addresses_to_check
         )
     )
     callee_code += Op.STOP
-    callee_address = pre.deploy_contract(callee_code, storage=callee_storage.canary())
+    callee_address = pre.deploy_contract(
+        callee_code,
+        storage={check_address: 0xDEADBEEF for check_address in addresses_to_check},
+    )
 
     tx = Transaction(
         gas_limit=1_000_000,
@@ -879,7 +962,7 @@ def test_account_warming(
     )
     post = {
         callee_address: Account(
-            storage=callee_storage,
+            storage=addresses_to_check,
         ),
     }
 


Reviews:
user: login: fselmo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T14:41:01Z
user: login: danceratopz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T19:16:47Z
user: login: fselmo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T19:35:46Z
user: login: fselmo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T19:43:19Z
user: login: fselmo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T21:43:23Z
user: login: marioevz
body: LGTM, I think the eels_resolutions.json change is ok, and I have this other PR #1454 to update to forks/prague once https://github.com/ethereum/execution-specs/pull/1191 is merged, but this temporary workaround should be ok.

Thanks!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T22:07:54Z

Review comments:
diff_hunk: @@ -20,6 +20,7 @@ Test fixtures for use by clients are available for each release on the [Github r
 ### 🧪 Test Cases
 
 - ✨  Test 7702 precompile case in same transaction as delegation without extra gas in case of precompile code execution; parametrize all call opcodes in existing precompile test ([#1431](https://github.com/ethereum/execution-spec-tests/pull/1431)).
+- ✨ [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Add tests for the case where Test precompile case in same transaction as delegation without extra gas in case of precompile code execution; parametrize all call opcodes in existing precompile test ([#1431](https://github.com/ethereum/execution-spec-tests/pull/1431)).
path: docs/CHANGELOG.md
user: login: fselmo
body: Why is this copying my bullet from above it 🤔 
created_at: 2025-04-11T14:41:01Z
author_association: COLLABORATOR
diff_hunk: @@ -20,6 +20,7 @@ Test fixtures for use by clients are available for each release on the [Github r
 ### 🧪 Test Cases
 
 - ✨  Test 7702 precompile case in same transaction as delegation without extra gas in case of precompile code execution; parametrize all call opcodes in existing precompile test ([#1431](https://github.com/ethereum/execution-spec-tests/pull/1431)).
+- ✨ [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702): Add tests for the case where Test precompile case in same transaction as delegation without extra gas in case of precompile code execution; parametrize all call opcodes in existing precompile test ([#1431](https://github.com/ethereum/execution-spec-tests/pull/1431)).
path: docs/CHANGELOG.md
user: login: danceratopz
body: That's a good question - fixed!
created_at: 2025-04-11T19:16:47Z
author_association: MEMBER
diff_hunk: @@ -435,6 +435,14 @@ def gas_test_parameter_args(
             },
             id="single_invalid_authorization_invalid_chain_id_single_signer",
         ),
+        pytest.param(
+            {
+                "authority_type": AddressType.EOA_WITH_SET_CODE,
path: tests/prague/eip7702_set_code_tx/test_gas.py
user: login: fselmo
body: Just looking now and had a quick q. I noticed this is not in the `if include_pre_authorized:` test cases. `EOA_WITH_SET_CODE` implies pre-authorized, right? If so, should this be `AddressType.EOA`? This passes all the tests but I want to make sure that's your intended test case.
created_at: 2025-04-11T19:35:45Z
author_association: COLLABORATOR
diff_hunk: @@ -435,6 +435,14 @@ def gas_test_parameter_args(
             },
             id="single_invalid_authorization_invalid_chain_id_single_signer",
         ),
+        pytest.param(
+            {
+                "authority_type": AddressType.EOA_WITH_SET_CODE,
path: tests/prague/eip7702_set_code_tx/test_gas.py
user: login: fselmo
body: nm, I wasn't looking at the code block in the description. I'll keep that in mind 😅.
created_at: 2025-04-11T19:43:19Z
author_association: COLLABORATOR
diff_hunk: @@ -435,6 +435,14 @@ def gas_test_parameter_args(
             },
             id="single_invalid_authorization_invalid_chain_id_single_signer",
         ),
+        pytest.param(
+            {
+                "authority_type": AddressType.EOA_WITH_SET_CODE,
path: tests/prague/eip7702_set_code_tx/test_gas.py
user: login: fselmo
body: I think I know at least some of what is going on. If you don't re-authorize this pre-authorized address, this first authorization is marked as `skip=True` ([here](https://github.com/ethereum/execution-spec-tests/blob/main/tests/prague/eip7702_set_code_tx/test_gas.py#L235-L240)), which, if there are no other authorizations in the transaction will yield `TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST` for most of the tests (since the only auth issued was skipped).

The rest of the tests are for `test_account_warming` and are failing with a `post` state verification mismatch.

I'm not quite sure what's best here and still getting used to all the intricacies of this test setup but I think the test may require `"authorizations_count": multiple_authorizations_count` to work well with `MULTIPLE_SIGNERS` and maybe even `"re_authorize": True` to work outside of the `include_pre_authorized` code block.

That still doesn't get the tests filling but I'm not sure if all the conditionals in the test setup are accounting for this test case in the first place. I know that adding the following test under `include_many` does work but I think some version of what I mentioned above should technically work inside the main `cases` block so it may be down to tweaking the test setup conditionals.

```py
if include_many:
    cases += [
        ...,
        pytest.param(
            {
                "authority_type": AddressType.EOA_WITH_SET_CODE,
                "re_authorize": True,
                "signer_type": SignerType.MULTIPLE_SIGNERS,
                "authorization_invalidity_type": AuthorizationInvalidityType.INVALID_NONCE,
                "authorizations_count": multiple_authorizations_count,
            },
            id="pre_authorized_eoa_authority_re_authorized_invalid_nonce_multiple_signers_multiple_auths",
        ),
    ]
```
created_at: 2025-04-11T21:43:23Z
author_association: COLLABORATOR

PR comments:
user: login: marioevz
created_at: 2025-04-11T22:58:19Z
body: Created PR #1444 to fix the logic of the test expectation for the two added tests, and added a new case.

Basically, because the delegation had an invalid nonce, the test expected that there was no delegation at the authority address, but that's not true because the type of the authority was `EOA_WITH_SET_CODE`, so the delegation was there even though the authorization failed.

Sorry about the confusing logic to this test, at this point we are close to re-implement the EVM in the tests.

I added some more comments to help understand the test a bit better.
author_association: MEMBER
user: login: danceratopz
created_at: 2025-04-15T09:27:41Z
body: This fills with the `7702-to-precompile` EELS branch at commit https://github.com/ethereum/execution-specs/pull/1191/commits/bbc469729ab095300b07de8fe323c3f9fac31857 from:  
- https://github.com/ethereum/execution-specs/pull/1191 

I diffed the fixtures against those from `main` as a sanity check and it LGTM. Contract code and storage has changed, as expected due to the use of addresses as storage keys, but the transactions remain unchanged :+1:.

@marioevz I updated `eels_resolver.json` as this EELS branch is the currently the best branch to fill Prague (`forks/prague` doesn't fill due to a rename of a t8n field, which has also been addressed in https://github.com/ethereum/execution-specs/pull/1191). We should update to `forks/prague` as soon as this PR been merged, but execution-specs are having some CI issues at the moment.

Feel free to revert the latest commit, if you'd rather leave the old eels branch.
author_association: MEMBER