From 9a3d55f8c75cd5aefd8c5a9a66f1d1529439bfbc Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 11 Jun 2024 23:39:58 +0000 Subject: [PATCH 01/23] feat(fw): Add type-4 transaction and authorization tuple type --- src/ethereum_test_fixtures/blockchain.py | 18 ++ src/ethereum_test_fixtures/state.py | 17 ++ .../tests/test_blockchain.py | 2 + src/ethereum_test_tools/__init__.py | 2 + src/ethereum_test_types/__init__.py | 2 + src/ethereum_test_types/types.py | 172 ++++++++++++++++-- 6 files changed, 196 insertions(+), 17 deletions(-) diff --git a/src/ethereum_test_fixtures/blockchain.py b/src/ethereum_test_fixtures/blockchain.py index d430d37bf3..01dd6d787d 100644 --- a/src/ethereum_test_fixtures/blockchain.py +++ b/src/ethereum_test_fixtures/blockchain.py @@ -26,6 +26,7 @@ from ethereum_test_exceptions import EngineAPIError, ExceptionInstanceOrList from ethereum_test_forks import Fork from ethereum_test_types.types import ( + AuthorizationTupleGeneric, ConsolidationRequest, ConsolidationRequestGeneric, DepositRequest, @@ -321,11 +322,28 @@ def from_fixture_header( return new_payload +class FixtureAuthorizationTuple(AuthorizationTupleGeneric[ZeroPaddedHexNumber]): + """ + Authorization tuple for fixture transactions. + """ + + @classmethod + def from_authorization_tuple( + cls, auth_tuple: AuthorizationTupleGeneric + ) -> "FixtureAuthorizationTuple": + """ + Returns a FixtureAuthorizationTuple from an AuthorizationTuple. + """ + return cls(**auth_tuple.model_dump()) + + class FixtureTransaction(TransactionFixtureConverter, TransactionGeneric[ZeroPaddedHexNumber]): """ Representation of an Ethereum transaction within a test Fixture. """ + authorization_tuples: List[FixtureAuthorizationTuple] | None = None + @classmethod def from_transaction(cls, tx: Transaction) -> "FixtureTransaction": """ diff --git a/src/ethereum_test_fixtures/state.py b/src/ethereum_test_fixtures/state.py index 514af9c8c7..8519d65339 100644 --- a/src/ethereum_test_fixtures/state.py +++ b/src/ethereum_test_fixtures/state.py @@ -10,6 +10,7 @@ from ethereum_test_exceptions import TransactionExceptionInstanceOrList from ethereum_test_types.types import ( AccessList, + AuthorizationTupleGeneric, CamelModel, EnvironmentGeneric, Transaction, @@ -28,6 +29,21 @@ class FixtureEnvironment(EnvironmentGeneric[ZeroPaddedHexNumber]): prev_randao: Hash | None = Field(None, alias="currentRandom") # type: ignore +class FixtureAuthorizationTuple(AuthorizationTupleGeneric[ZeroPaddedHexNumber]): + """ + Authorization tuple for fixture transactions. + """ + + @classmethod + def from_authorization_tuple( + cls, auth_tuple: AuthorizationTupleGeneric + ) -> "FixtureAuthorizationTuple": + """ + Returns a FixtureAuthorizationTuple from an AuthorizationTuple. + """ + return cls(**auth_tuple.model_dump()) + + class FixtureTransaction(TransactionFixtureConverter): """ Type used to describe a transaction in a state test. @@ -42,6 +58,7 @@ class FixtureTransaction(TransactionFixtureConverter): value: List[ZeroPaddedHexNumber] data: List[Bytes] access_lists: List[List[AccessList]] | None = None + authorization_tuples: List[FixtureAuthorizationTuple] | None = None max_fee_per_blob_gas: ZeroPaddedHexNumber | None = None blob_versioned_hashes: Sequence[Hash] | None = None sender: Address | None = None diff --git a/src/ethereum_test_fixtures/tests/test_blockchain.py b/src/ethereum_test_fixtures/tests/test_blockchain.py index c1772d3fbe..6a03decc39 100644 --- a/src/ethereum_test_fixtures/tests/test_blockchain.py +++ b/src/ethereum_test_fixtures/tests/test_blockchain.py @@ -22,6 +22,7 @@ from ethereum_test_forks import Prague from ethereum_test_types import ( AccessList, + AuthorizationTuple, ConsolidationRequest, DepositRequest, Requests, @@ -32,6 +33,7 @@ from ..blockchain import ( EngineNewPayloadParameters, + FixtureAuthorizationTuple, FixtureBlockBase, FixtureEngineNewPayload, FixtureExecutionPayload, diff --git a/src/ethereum_test_tools/__init__.py b/src/ethereum_test_tools/__init__.py index 2d6d16c9a8..5d5750b7c8 100644 --- a/src/ethereum_test_tools/__init__.py +++ b/src/ethereum_test_tools/__init__.py @@ -37,6 +37,7 @@ EOA, AccessList, Alloc, + AuthorizationTuple, ConsolidationRequest, DepositRequest, Environment, @@ -82,6 +83,7 @@ "Account", "Address", "Alloc", + "AuthorizationTuple", "BaseFixture", "BaseTest", "Block", diff --git a/src/ethereum_test_types/__init__.py b/src/ethereum_test_types/__init__.py index 16714bf4f4..b8b0cf5aba 100644 --- a/src/ethereum_test_types/__init__.py +++ b/src/ethereum_test_types/__init__.py @@ -18,6 +18,7 @@ AccessList, Account, Alloc, + AuthorizationTuple, CamelModel, ConsolidationRequest, DepositRequest, @@ -34,6 +35,7 @@ "AccessList", "Account", "Alloc", + "AuthorizationTuple", "CamelModel", "ConsolidationRequest", "DepositRequest", diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 269f7073ab..536f1c3796 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from functools import cached_property -from typing import Any, Dict, Generic, List, Sequence +from typing import Any, ClassVar, Dict, Generic, List, Sequence, Tuple from coincurve.keys import PrivateKey, PublicKey from ethereum import rlp as eth_rlp @@ -457,6 +457,115 @@ def to_list(self) -> List[Address | List[Hash]]: return [self.address, self.storage_keys] +class AuthorizationTupleGeneric(CamelModel, Generic[NumberBoundTypeVar]): + """ + Authorization tuple for transactions. + """ + + chain_id: NumberBoundTypeVar = Field(1) # type: ignore + address: Address + nonce: List[NumberBoundTypeVar] | NumberBoundTypeVar | None = None + + v: NumberBoundTypeVar = Field(0) # type: ignore + r: NumberBoundTypeVar = Field(0) # type: ignore + s: NumberBoundTypeVar = Field(0) # type: ignore + + magic: ClassVar[int] = 0x05 + + def model_post_init(self, __context: Any) -> None: + """ + Automatically converts the nonce to a list if it is not already. + """ + super().model_post_init(__context) + if self.nonce is not None and not isinstance(self.nonce, list): + self.nonce = [self.nonce] + + def nonce_list(self) -> List[Uint]: + """ + Returns the nonce as a list. + """ + if self.nonce is None: + return [] + return ( + [Uint(n) for n in self.nonce] if isinstance(self.nonce, list) else [Uint(self.nonce)] + ) + + def to_list(self) -> List[Any]: + """ + Returns the authorization tuple as a list of serializable elements. + """ + return [ + Uint(self.chain_id), + self.address, + self.nonce_list(), + Uint(self.v), + Uint(self.r), + Uint(self.s), + ] + + def signing_hash(self) -> Hash: + """ + Returns the data to be signed. + """ + return Hash( + keccak256( + int.to_bytes(self.magic, length=1, byteorder="big") + + eth_rlp.encode( + [ + Uint(self.chain_id), + self.address, + self.nonce_list(), + ] + ) + ) + ) + + def signature(self, private_key: Hash) -> Tuple[int, int, int]: + """ + Returns the signature of the authorization tuple. + """ + signing_hash = self.signing_hash() + signature_bytes = PrivateKey(secret=private_key).sign_recoverable( + signing_hash, hasher=None + ) + return ( + signature_bytes[64], + int.from_bytes(signature_bytes[0:32], byteorder="big"), + int.from_bytes(signature_bytes[32:64], byteorder="big"), + ) + + +class AuthorizationTuple(AuthorizationTupleGeneric[HexNumber]): + """ + Authorization tuple for transactions. + """ + + signer: EOA | None = None + secret_key: Hash | None = None + + def model_post_init(self, __context: Any) -> None: + """ + Automatically signs the authorization tuple if a secret key or sender are provided. + """ + super().model_post_init(__context) + + if self.secret_key is not None: + self.sign(self.secret_key) + elif self.signer is not None: + assert self.signer.key is not None, "signer must have a key" + self.sign(self.signer.key) + + def sign(self, private_key: Hash) -> None: + """ + Signs the authorization tuple with a private key. + """ + signature = self.signature(private_key) + + self.v = HexNumber(signature[0]) + self.r = HexNumber(signature[1]) + self.s = HexNumber(signature[2]) + + class TransactionGeneric(BaseModel, Generic[NumberBoundTypeVar]): """ Generic transaction type used as a parent for Transaction and FixtureTransaction (blockchain). @@ -547,6 +656,8 @@ class Transaction(TransactionGeneric[HexNumber], TransactionTransitionToolConver to: Address | None = Field(Address(0xAA)) data: Bytes = Field(Bytes(b""), alias="input") + authorization_tuples: List[AuthorizationTuple] | None = None + secret_key: Hash | None = None error: List[TransactionException] | TransactionException | None = Field(None, exclude=True) @@ -594,7 +705,9 @@ def model_post_init(self, __context): if "ty" not in self.model_fields_set: # Try to deduce transaction type from included fields - if self.max_fee_per_blob_gas is not None or self.blob_kzg_commitments is not None: + if self.authorization_tuples is not None: + self.ty = 4 + elif self.max_fee_per_blob_gas is not None or self.blob_kzg_commitments is not None: self.ty = 3 elif self.max_fee_per_gas is not None or self.max_priority_fee_per_gas is not None: self.ty = 2 @@ -627,6 +740,9 @@ def model_post_init(self, __context): if self.ty == 3 and self.max_fee_per_blob_gas is None: self.max_fee_per_blob_gas = 1 + if self.ty == 4 and self.authorization_tuples is None: + self.authorization_tuples = [] + if "nonce" not in self.model_fields_set and self.sender is not None: self.nonce = HexNumber(self.sender.get_nonce()) @@ -710,18 +826,40 @@ def signing_envelope(self) -> List[Any]: Returns the list of values included in the envelope used for signing. """ to = self.to if self.to else bytes() - if self.ty == 3: + if self.ty == 4: + # EIP-7702: https://eips.ethereum.org/EIPS/eip-7702 + if self.max_priority_fee_per_gas is None: + raise ValueError(f"max_priority_fee_per_gas must be set for type {self.ty} tx") + if self.max_fee_per_gas is None: + raise ValueError(f"max_fee_per_gas must be set for type {self.ty} tx") + if self.access_list is None: + raise ValueError(f"access_list must be set for type {self.ty} tx") + if self.authorization_tuples is None: + raise ValueError(f"authorization_tuples must be set for type {self.ty} tx") + return [ + Uint(self.chain_id), + Uint(self.nonce), + Uint(self.max_priority_fee_per_gas), + Uint(self.max_fee_per_gas), + Uint(self.gas_limit), + to, + Uint(self.value), + self.data, + [a.to_list() for a in self.access_list], + [a.to_list() for a in self.authorization_tuples], + ] + elif self.ty == 3: # EIP-4844: https://eips.ethereum.org/EIPS/eip-4844 if self.max_priority_fee_per_gas is None: - raise ValueError("max_priority_fee_per_gas must be set for type 3 tx") + raise ValueError(f"max_priority_fee_per_gas must be set for type {self.ty} tx") if self.max_fee_per_gas is None: - raise ValueError("max_fee_per_gas must be set for type 3 tx") + raise ValueError(f"max_fee_per_gas must be set for type {self.ty} tx") if self.max_fee_per_blob_gas is None: - raise ValueError("max_fee_per_blob_gas must be set for type 3 tx") + raise ValueError(f"max_fee_per_blob_gas must be set for type {self.ty} tx") if self.blob_versioned_hashes is None: - raise ValueError("blob_versioned_hashes must be set for type 3 tx") + raise ValueError(f"blob_versioned_hashes must be set for type {self.ty} tx") if self.access_list is None: - raise ValueError("access_list must be set for type 3 tx") + raise ValueError(f"access_list must be set for type {self.ty} tx") return [ Uint(self.chain_id), Uint(self.nonce), @@ -738,11 +876,11 @@ def signing_envelope(self) -> List[Any]: elif self.ty == 2: # EIP-1559: https://eips.ethereum.org/EIPS/eip-1559 if self.max_priority_fee_per_gas is None: - raise ValueError("max_priority_fee_per_gas must be set for type 2 tx") + raise ValueError(f"max_priority_fee_per_gas must be set for type {self.ty} tx") if self.max_fee_per_gas is None: - raise ValueError("max_fee_per_gas must be set for type 2 tx") + raise ValueError(f"max_fee_per_gas must be set for type {self.ty} tx") if self.access_list is None: - raise ValueError("access_list must be set for type 2 tx") + raise ValueError(f"access_list must be set for type {self.ty} tx") return [ Uint(self.chain_id), Uint(self.nonce), @@ -757,9 +895,9 @@ def signing_envelope(self) -> List[Any]: elif self.ty == 1: # EIP-2930: https://eips.ethereum.org/EIPS/eip-2930 if self.gas_price is None: - raise ValueError("gas_price must be set for type 1 tx") + raise ValueError(f"gas_price must be set for type {self.ty} tx") if self.access_list is None: - raise ValueError("access_list must be set for type 1 tx") + raise ValueError(f"access_list must be set for type {self.ty} tx") return [ Uint(self.chain_id), @@ -773,7 +911,7 @@ def signing_envelope(self) -> List[Any]: ] elif self.ty == 0: if self.gas_price is None: - raise ValueError("gas_price must be set for type 0 tx") + raise ValueError(f"gas_price must be set for type {self.ty} tx") if self.protected: # EIP-155: https://eips.ethereum.org/EIPS/eip-155 @@ -815,11 +953,11 @@ def payload_body(self) -> List[Any]: elif self.ty == 3 and self.wrapped_blob_transaction: # EIP-4844: https://eips.ethereum.org/EIPS/eip-4844 if self.blobs is None: - raise ValueError("blobs must be set for type 3 tx") + raise ValueError(f"blobs must be set for type {self.ty} tx") if self.blob_kzg_commitments is None: - raise ValueError("blob_kzg_commitments must be set for type 3 tx") + raise ValueError(f"blob_kzg_commitments must be set for type {self.ty} tx") if self.blob_kzg_proofs is None: - raise ValueError("blob_kzg_proofs must be set for type 3 tx") + raise ValueError(f"blob_kzg_proofs must be set for type {self.ty} tx") return [ signing_envelope + [Uint(self.v), Uint(self.r), Uint(self.s)], list(self.blobs), From 081a02813c86727ec2f648ef3a137f41ca0e85c6 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 11 Jun 2024 23:43:29 +0000 Subject: [PATCH 02/23] new(tests): Add EIP-7702 folder, first test --- tests/prague/eip7702_eoa_code_tx/__init__.py | 3 + tests/prague/eip7702_eoa_code_tx/spec.py | 29 ++++++++++ .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 55 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 tests/prague/eip7702_eoa_code_tx/__init__.py create mode 100644 tests/prague/eip7702_eoa_code_tx/spec.py create mode 100644 tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py diff --git a/tests/prague/eip7702_eoa_code_tx/__init__.py b/tests/prague/eip7702_eoa_code_tx/__init__.py new file mode 100644 index 0000000000..1400f0640a --- /dev/null +++ b/tests/prague/eip7702_eoa_code_tx/__init__.py @@ -0,0 +1,3 @@ +""" +Cross-client EIP-7702 Tests +""" diff --git a/tests/prague/eip7702_eoa_code_tx/spec.py b/tests/prague/eip7702_eoa_code_tx/spec.py new file mode 100644 index 0000000000..f0da9cbdb8 --- /dev/null +++ b/tests/prague/eip7702_eoa_code_tx/spec.py @@ -0,0 +1,29 @@ +""" +Defines EIP-7702 specification constants and functions. +""" +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ReferenceSpec: + """ + Defines the reference spec version and git path. + """ + + git_path: str + version: str + + +ref_spec_7702 = ReferenceSpec("EIPS/eip-7702.md", "7357ff1f3f176aada6d350d6e42a292a3dec27f4") + + +@dataclass(frozen=True) +class Spec: + """ + Parameters from the EIP-7702 specifications as defined at + https://eips.ethereum.org/EIPS/eip-7702 + """ + + SET_CODE_TX_TYPE = 0x04 + MAGIC = 0x05 + PER_AUTH_BASE_COST = 2500 diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py new file mode 100644 index 0000000000..fcde1cb37a --- /dev/null +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -0,0 +1,55 @@ +""" +abstract: Tests use of set-code transactions from [EIP-7702: Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) + Tests use of set-code transactions from [EIP-7702: Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702). +""" # noqa: E501 + +import pytest + +from ethereum_test_tools import Alloc, AuthorizationTuple, Environment +from ethereum_test_tools import Opcodes as Op +from ethereum_test_tools import StateTestFiller, Transaction + +from .spec import ref_spec_7702 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7702.git_path +REFERENCE_SPEC_VERSION = ref_spec_7702.version + +pytestmark = [ + pytest.mark.valid_from("Prague"), +] + + +def test_set_code_to_self_destruct( + state_test: StateTestFiller, + pre: Alloc, +): + """ + Test the executing self-destruct opcode in a set-code transaction. + """ + set_code_to_address = pre.deploy_contract(Op.SELFDESTRUCT(Op.ADDRESS)) + + signer = pre.fund_eoa(0) + + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=sender, + value=0, + authorization_tuples=[ + AuthorizationTuple( + chain_id=1, + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={}, + ) From d2f127d58b3aad7eb419b6860ab4c1913d32f037 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 24 Jun 2024 23:07:44 +0000 Subject: [PATCH 03/23] tests: create contract --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 74 +++++++++++++++++-- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index fcde1cb37a..6ab09c1d69 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -5,18 +5,16 @@ import pytest -from ethereum_test_tools import Alloc, AuthorizationTuple, Environment +from ethereum_test_tools import Account, Alloc, AuthorizationTuple, Environment, Initcode from ethereum_test_tools import Opcodes as Op -from ethereum_test_tools import StateTestFiller, Transaction +from ethereum_test_tools import StateTestFiller, Storage, Transaction, compute_create_address from .spec import ref_spec_7702 REFERENCE_SPEC_GIT_PATH = ref_spec_7702.git_path REFERENCE_SPEC_VERSION = ref_spec_7702.version -pytestmark = [ - pytest.mark.valid_from("Prague"), -] +pytestmark = pytest.mark.valid_from("Prague") def test_set_code_to_self_destruct( @@ -26,19 +24,74 @@ def test_set_code_to_self_destruct( """ Test the executing self-destruct opcode in a set-code transaction. """ + signer = pre.fund_eoa(0) + set_code_to_address = pre.deploy_contract(Op.SELFDESTRUCT(Op.ADDRESS)) + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=sender, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={}, + ) + + +@pytest.mark.parametrize( + "op", + [ + Op.CREATE, + Op.CREATE2, + ], +) +def test_set_code_to_contract_creator( + state_test: StateTestFiller, + pre: Alloc, + op: Op, +): + """ + Test the executing self-destruct opcode in a set-code transaction. + """ + storage = Storage() signer = pre.fund_eoa(0) + deployed_code = Op.STOP + initcode = Initcode(deploy_code=deployed_code) + + deployed_contract_address = compute_create_address(signer) + + set_code = Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + Op.SSTORE( + storage.store_next(deployed_contract_address), + op(value=0, offset=0, size=Op.CALLDATASIZE), + ) + set_code_to_address = pre.deploy_contract( + set_code, + ) + sender = pre.fund_eoa(10**18) tx = Transaction( gas_limit=1_000_000, to=sender, value=0, + data=initcode, authorization_tuples=[ AuthorizationTuple( - chain_id=1, address=set_code_to_address, nonce=0, signer=signer, @@ -51,5 +104,12 @@ def test_set_code_to_self_destruct( env=Environment(), pre=pre, tx=tx, - post={}, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=1, code=b"", storage=storage), + deployed_contract_address: Account( + code=deployed_code, + storage={}, + ), + }, ) From b4c294e87d07d891573bff5036f9d55607aaa9ce Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 25 Jun 2024 01:29:55 +0000 Subject: [PATCH 04/23] new(tests): more 7702 tests --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 354 +++++++++++++++++- 1 file changed, 349 insertions(+), 5 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 6ab09c1d69..1acc20c9c8 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -5,9 +5,23 @@ import pytest -from ethereum_test_tools import Account, Alloc, AuthorizationTuple, Environment, Initcode +from ethereum_test_tools import ( + Account, + Alloc, + AuthorizationTuple, + Bytecode, + Conditional, + Environment, + Initcode, +) from ethereum_test_tools import Opcodes as Op -from ethereum_test_tools import StateTestFiller, Storage, Transaction, compute_create_address +from ethereum_test_tools import ( + StateTestFiller, + Storage, + Transaction, + compute_create2_address, + compute_create_address, +) from .spec import ref_spec_7702 @@ -17,6 +31,65 @@ pytestmark = pytest.mark.valid_from("Prague") +@pytest.mark.parametrize( + "suffix,succeeds", + [ + (Op.STOP, True), + (Op.RETURN(0, 0), True), + (Op.REVERT, False), + (Op.INVALID, False), + ], + ids=["stop", "revert", "invalid"], +) +def test_set_code_to_self_sstore( + state_test: StateTestFiller, + pre: Alloc, + suffix: Bytecode, + succeeds: bool, +): + """ + Test the executing a simple SSTORE in a set-code transaction. + """ + storage = Storage() + signer = pre.fund_eoa(0) + + set_code = ( + Op.SSTORE(storage.store_next(1), 1) + + Op.SSTORE(storage.store_next(2), 2) + + Op.SSTORE(storage.store_next(3), 3) + + suffix + ) + set_code_to_address = pre.deploy_contract( + set_code, + ) + + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=0, code=b"", storage=storage if succeeds else {}), + }, + ) + + def test_set_code_to_self_destruct( state_test: StateTestFiller, pre: Alloc, @@ -32,7 +105,7 @@ def test_set_code_to_self_destruct( tx = Transaction( gas_limit=1_000_000, - to=sender, + to=signer, value=0, authorization_tuples=[ AuthorizationTuple( @@ -65,7 +138,7 @@ def test_set_code_to_contract_creator( op: Op, ): """ - Test the executing self-destruct opcode in a set-code transaction. + Test the executing a contract-creating opcode in a set-code transaction. """ storage = Storage() signer = pre.fund_eoa(0) @@ -87,7 +160,7 @@ def test_set_code_to_contract_creator( tx = Transaction( gas_limit=1_000_000, - to=sender, + to=signer, value=0, data=initcode, authorization_tuples=[ @@ -113,3 +186,274 @@ def test_set_code_to_contract_creator( ), }, ) + + +@pytest.mark.parametrize( + "op", + [ + Op.CALL, + Op.DELEGATECALL, + Op.STATICCALL, + Op.CALLCODE, + ], +) +@pytest.mark.parametrize( + "value", + [ + 0, + 10**18, + ], +) +def test_set_code_to_self_caller( + state_test: StateTestFiller, + pre: Alloc, + op: Op, + value: int, +): + """ + Test the executing a self-call in a set-code transaction. + """ + storage = Storage() + signer = pre.fund_eoa(0) + + first_entry_slot = 1 + re_entry_success_slot = 2 + re_entry_call_return_code_slot = 3 + set_code = Conditional( + condition=Op.ISZERO(Op.SLOAD(first_entry_slot)), + if_true=Op.SSTORE(first_entry_slot, 1) + + Op.SSTORE(re_entry_call_return_code_slot, op(address=signer, value=value)) + + Op.STOP, + if_false=Op.SSTORE(re_entry_success_slot, 1) + Op.STOP, + ) + storage[first_entry_slot] = True + storage[re_entry_success_slot] = op != Op.STATICCALL + storage[re_entry_call_return_code_slot] = op != Op.STATICCALL + set_code_to_address = pre.deploy_contract( + set_code, + ) + + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=value, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=0, code=b"", storage=storage, balance=value), + }, + ) + + +def test_address_from_set_code( + state_test: StateTestFiller, + pre: Alloc, +): + """ + Test the address opcode in a set-code transaction. + """ + storage = Storage() + signer = pre.fund_eoa(0) + + set_code = Op.SSTORE(storage.store_next(signer), Op.ADDRESS) + Op.STOP + set_code_to_address = pre.deploy_contract(set_code) + + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=0, code=b"", storage=storage), + }, + ) + + +def test_ext_code_on_set_code( + state_test: StateTestFiller, + pre: Alloc, + value: int, + balance: int, +): + """ + Test different ext*code operations on a set-code address. + """ + signer = pre.fund_eoa(balance) + + slot_call_success = 1 + slot_caller = 2 + slot_ext_code_size_result = 3 + slot_ext_code_hash_result = 4 + slot_ext_code_copy_result = 5 + slot_ext_balance_result = 6 + + callee_code = ( + Op.SSTORE(slot_caller, Op.CALLER) + + Op.SSTORE(slot_ext_code_size_result) + + Op.EXTCODESIZE(Op.CALLER) + + Op.SSTORE(slot_ext_code_hash_result) + + Op.EXTCODEHASH(Op.CALLER) + + Op.EXTCODECOPY(Op.CALLER, 0, 0, Op.EXTCODESIZE(Op.CALLER)) + + Op.SSTORE(slot_ext_code_copy_result, Op.MLOAD(0)) + + Op.SSTORE(slot_ext_balance_result, Op.BALANCE(Op.CALLER)) + + Op.STOP + ) + callee_address = pre.deploy_contract(callee_code) + callee_storage = Storage() + + set_code = Op.SSTORE(slot_call_success, Op.CALL(address=callee_address)) + Op.STOP + set_code_to_address = pre.deploy_contract(set_code) + + callee_storage[slot_caller] = signer + callee_storage[slot_ext_code_size_result] = len(set_code) + callee_storage[slot_ext_code_hash_result] = set_code.keccak256() + callee_storage[slot_ext_code_copy_result] = bytes(set_code).ljust(32, b"\x00")[:32] + callee_storage[slot_ext_balance_result] = balance + + sender = pre.fund_eoa(10**18) + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=value, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=sender, + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=0, code=b"", storage={}, balance=value), + callee_address: Account(storage=callee_storage), + }, + ) + + +@pytest.mark.parametrize( + "create_op", + [ + Op.CREATE, + Op.CREATE2, + ], +) +def test_set_code_to_account_deployed_in_same_tx( + state_test: StateTestFiller, + pre: Alloc, + create_op: Op, +): + """ + Test setting the code of an account to an address that is deployed in the same transaction, + and test calling the set-code address and the deployed contract. + """ + signer = pre.fund_eoa(0) + + success_slot = 1 + + deployed_code = Op.SSTORE(success_slot, 1) + Op.STOP + initcode = Initcode(deploy_code=deployed_code) + + deployed_contract_address_slot = 1 + signer_call_return_code_slot = 2 + deployed_contract_call_return_code_slot = 3 + + contract_creator_code = ( + Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + + Op.SSTORE(deployed_contract_address_slot, create_op(offset=0, size=Op.CALLDATASIZE)) + + Op.SSTORE(signer_call_return_code_slot, Op.CALL(address=signer)) + + Op.SSTORE( + deployed_contract_call_return_code_slot, + Op.CALL(address=Op.SLOAD(deployed_contract_address_slot)), + ) + + Op.STOP() + ) + contract_creator_address = pre.deploy_contract(contract_creator_code) + + if create_op == Op.CREATE: + deployed_contract_address = compute_create_address( + address=contract_creator_address, + nonce=1, + ) + else: + deployed_contract_address = compute_create2_address( + address=contract_creator_address, + salt=0, + initcode=initcode, + ) + + tx = Transaction( + gas_limit=1_000_000, + to=contract_creator_address, + value=0, + data=initcode, + authorization_tuples=[ + AuthorizationTuple( + address=deployed_contract_address, + nonce=0, + signer=signer, + ), + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + deployed_contract_address: Account( + storage={success_slot: 1}, + ), + signer: Account( + nonce=0, + code=b"", + storage={}, + ), + contract_creator_address: Account( + storage={ + deployed_contract_address_slot: deployed_contract_address, + signer_call_return_code_slot: 1, + deployed_contract_call_return_code_slot: 1, + } + ), + }, + ) From 931f48eecb380c95984470e5c9e70141585b743e Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 26 Jun 2024 00:45:53 +0000 Subject: [PATCH 05/23] new(tests): EIP-7702 --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 341 +++++++++++++++--- 1 file changed, 294 insertions(+), 47 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 1acc20c9c8..aef9f242de 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -3,6 +3,9 @@ Tests use of set-code transactions from [EIP-7702: Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702). """ # noqa: E501 +from enum import Enum +from itertools import count + import pytest from ethereum_test_tools import ( @@ -31,21 +34,31 @@ pytestmark = pytest.mark.valid_from("Prague") +class InvalidityReasons(Enum): + """ + Reasons for invalidity. + """ + + NONCE = "nonce" + MULTIPLE_NONCE = "multiple_nonce" + CHAIN_ID = "chain_id" + + @pytest.mark.parametrize( "suffix,succeeds", [ - (Op.STOP, True), - (Op.RETURN(0, 0), True), - (Op.REVERT, False), - (Op.INVALID, False), + pytest.param(Op.STOP, True, id="stop"), + pytest.param(Op.RETURN(0, 0), True, id="return"), + pytest.param(Op.REVERT, False, id="revert"), + pytest.param(Op.INVALID, False, id="invalid"), ], - ids=["stop", "revert", "invalid"], ) -def test_set_code_to_self_sstore( +def test_set_code_to_sstore( state_test: StateTestFiller, pre: Alloc, suffix: Bytecode, succeeds: bool, + chain_id: int, ): """ Test the executing a simple SSTORE in a set-code transaction. @@ -63,8 +76,6 @@ def test_set_code_to_self_sstore( set_code, ) - sender = pre.fund_eoa(10**18) - tx = Transaction( gas_limit=1_000_000, to=signer, @@ -76,7 +87,7 @@ def test_set_code_to_self_sstore( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -84,7 +95,7 @@ def test_set_code_to_self_sstore( pre=pre, tx=tx, post={ - set_code_to_address: Account(storage={}), + set_code_to_address: Account(storage={k: 0 for k in storage}), signer: Account(nonce=0, code=b"", storage=storage if succeeds else {}), }, ) @@ -101,8 +112,6 @@ def test_set_code_to_self_destruct( set_code_to_address = pre.deploy_contract(Op.SELFDESTRUCT(Op.ADDRESS)) - sender = pre.fund_eoa(10**18) - tx = Transaction( gas_limit=1_000_000, to=signer, @@ -114,7 +123,7 @@ def test_set_code_to_self_destruct( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -156,8 +165,6 @@ def test_set_code_to_contract_creator( set_code, ) - sender = pre.fund_eoa(10**18) - tx = Transaction( gas_limit=1_000_000, to=signer, @@ -170,7 +177,7 @@ def test_set_code_to_contract_creator( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -216,9 +223,9 @@ def test_set_code_to_self_caller( storage = Storage() signer = pre.fund_eoa(0) - first_entry_slot = 1 - re_entry_success_slot = 2 - re_entry_call_return_code_slot = 3 + first_entry_slot = storage.store_next(True) + re_entry_success_slot = storage.store_next(op != Op.STATICCALL) + re_entry_call_return_code_slot = storage.store_next(op != Op.STATICCALL) set_code = Conditional( condition=Op.ISZERO(Op.SLOAD(first_entry_slot)), if_true=Op.SSTORE(first_entry_slot, 1) @@ -226,14 +233,7 @@ def test_set_code_to_self_caller( + Op.STOP, if_false=Op.SSTORE(re_entry_success_slot, 1) + Op.STOP, ) - storage[first_entry_slot] = True - storage[re_entry_success_slot] = op != Op.STATICCALL - storage[re_entry_call_return_code_slot] = op != Op.STATICCALL - set_code_to_address = pre.deploy_contract( - set_code, - ) - - sender = pre.fund_eoa(10**18) + set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( gas_limit=1_000_000, @@ -246,7 +246,7 @@ def test_set_code_to_self_caller( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -260,6 +260,91 @@ def test_set_code_to_self_caller( ) +@pytest.mark.parametrize( + "op", + [ + Op.CALL, + Op.DELEGATECALL, + Op.STATICCALL, + Op.CALLCODE, + ], +) +@pytest.mark.parametrize( + "value", + [ + 0, + 10**18, + ], +) +def test_set_code_to_set_code_caller( + state_test: StateTestFiller, + pre: Alloc, + op: Op, + value: int, +): + """ + Test the calling a set-code account from another set-code account. + """ + signer_1 = pre.fund_eoa(0) + storage_1 = Storage() + + set_code_1_call_result_slot = storage_1.store_next(op != Op.STATICCALL) + set_code_1_success = storage_1.store_next(True) + + signer_2 = pre.fund_eoa(0) + storage_2 = Storage().with_start_slot(storage_1.next_slot()) + set_code_2_success = storage_2.store_next(op != Op.STATICCALL) + + set_code_1 = ( + Op.SSTORE(set_code_1_call_result_slot, op(address=signer_2, value=value)) + + Op.SSTORE(set_code_1_success, 1) + + Op.STOP + ) + set_code_to_address_1 = pre.deploy_contract(set_code_1) + + set_code_2 = Op.SSTORE(set_code_2_success, 1) + Op.STOP + set_code_to_address_2 = pre.deploy_contract(set_code_2) + + tx = Transaction( + gas_limit=1_000_000, + to=signer_1, + value=value, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address_1, + nonce=0, + signer=signer_1, + ), + AuthorizationTuple( + address=set_code_to_address_2, + nonce=0, + signer=signer_2, + ), + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address_1: Account(storage={k: 0 for k in storage_1}), + set_code_to_address_2: Account(storage={k: 0 for k in storage_2}), + signer_1: Account( + nonce=0, + storage=storage_1 if op != Op.DELEGATECALL else storage_1 + storage_2, + balance=0 if op == Op.CALL else value, + ), + signer_2: Account( + nonce=0, + storage=storage_2 if op != Op.DELEGATECALL else {}, + balance=value if op == Op.CALL else 0, + ), + }, + ) + + def test_address_from_set_code( state_test: StateTestFiller, pre: Alloc, @@ -273,8 +358,6 @@ def test_address_from_set_code( set_code = Op.SSTORE(storage.store_next(signer), Op.ADDRESS) + Op.STOP set_code_to_address = pre.deploy_contract(set_code) - sender = pre.fund_eoa(10**18) - tx = Transaction( gas_limit=1_000_000, to=signer, @@ -286,7 +369,7 @@ def test_address_from_set_code( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -300,10 +383,16 @@ def test_address_from_set_code( ) +@pytest.mark.parametrize( + "balance", + [ + 0, + 10**18, + ], +) def test_ext_code_on_set_code( state_test: StateTestFiller, pre: Alloc, - value: int, balance: int, ): """ @@ -311,19 +400,18 @@ def test_ext_code_on_set_code( """ signer = pre.fund_eoa(balance) - slot_call_success = 1 - slot_caller = 2 - slot_ext_code_size_result = 3 - slot_ext_code_hash_result = 4 - slot_ext_code_copy_result = 5 - slot_ext_balance_result = 6 + slot = count(1) + slot_call_success = next(slot) + slot_caller = next(slot) + slot_ext_code_size_result = next(slot) + slot_ext_code_hash_result = next(slot) + slot_ext_code_copy_result = next(slot) + slot_ext_balance_result = next(slot) callee_code = ( Op.SSTORE(slot_caller, Op.CALLER) - + Op.SSTORE(slot_ext_code_size_result) - + Op.EXTCODESIZE(Op.CALLER) - + Op.SSTORE(slot_ext_code_hash_result) - + Op.EXTCODEHASH(Op.CALLER) + + Op.SSTORE(slot_ext_code_size_result, Op.EXTCODESIZE(Op.CALLER)) + + Op.SSTORE(slot_ext_code_hash_result, Op.EXTCODEHASH(Op.CALLER)) + Op.EXTCODECOPY(Op.CALLER, 0, 0, Op.EXTCODESIZE(Op.CALLER)) + Op.SSTORE(slot_ext_code_copy_result, Op.MLOAD(0)) + Op.SSTORE(slot_ext_balance_result, Op.BALANCE(Op.CALLER)) @@ -341,12 +429,9 @@ def test_ext_code_on_set_code( callee_storage[slot_ext_code_copy_result] = bytes(set_code).ljust(32, b"\x00")[:32] callee_storage[slot_ext_balance_result] = balance - sender = pre.fund_eoa(10**18) - tx = Transaction( gas_limit=1_000_000, to=signer, - value=value, authorization_tuples=[ AuthorizationTuple( address=set_code_to_address, @@ -354,7 +439,7 @@ def test_ext_code_on_set_code( signer=signer, ), ], - sender=sender, + sender=pre.fund_eoa(10**18), ) state_test( @@ -363,12 +448,72 @@ def test_ext_code_on_set_code( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=0, code=b"", storage={}, balance=value), + signer: Account(nonce=0, code=b"", storage={}, balance=balance), callee_address: Account(storage=callee_storage), }, ) +@pytest.mark.parametrize( + "balance", + [ + 0, + 10**18, + ], +) +def test_self_code_on_set_code( + state_test: StateTestFiller, + pre: Alloc, + balance: int, +): + """ + Test codesize and codecopy operations on a set-code address. + """ + signer = pre.fund_eoa(balance) + + slot = count(1) + slot_code_size_result = next(slot) + slot_code_copy_result = next(slot) + slot_self_balance_result = next(slot) + + set_code = ( + Op.SSTORE(slot_code_size_result, Op.CODESIZE) + + Op.CODECOPY(0, 0, Op.CODESIZE) + + Op.SSTORE(slot_code_copy_result, Op.MLOAD(0)) + + Op.SSTORE(slot_self_balance_result, Op.SELFBALANCE) + + Op.STOP + ) + set_code_to_address = pre.deploy_contract(set_code) + + storage = Storage() + storage[slot_code_size_result] = len(set_code) + storage[slot_code_copy_result] = bytes(set_code).ljust(32, b"\x00")[:32] + storage[slot_self_balance_result] = balance + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=signer, + ), + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + set_code_to_address: Account(storage={}), + signer: Account(nonce=0, code=b"", storage={}, balance=balance), + }, + ) + + @pytest.mark.parametrize( "create_op", [ @@ -457,3 +602,105 @@ def test_set_code_to_account_deployed_in_same_tx( ), }, ) + + +def test_set_code_multiple_valid_authorization_tuples_same_signer( + state_test: StateTestFiller, + pre: Alloc, +): + """ + Test setting the code of an account with multiple authorization tuples from the same signer. + """ + signer = pre.fund_eoa(0) + + success_slot = 1 + + tuple_count = 10 + + addresses = [ + pre.deploy_contract(Op.SSTORE(success_slot, i + 1) + Op.STOP) for i in range(tuple_count) + ] + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=address, + nonce=0, + signer=signer, + ) + for address in addresses + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + signer: Account( + nonce=1, + code=b"", + storage={ + success_slot: 1, + }, + ), + }, + ) + + +@pytest.mark.parametrize( + "invalidity_reason", + list(InvalidityReasons), +) +def test_set_code_invalid_authorization_tuple( + state_test: StateTestFiller, + pre: Alloc, + invalidity_reason: InvalidityReasons, +): + """ + Test attempting to set the code of an account with invalid authorization tuple. + """ + signer = pre.fund_eoa(0) + + success_slot = 1 + + set_code = Op.SSTORE(success_slot, 1) + Op.STOP + set_code_to_address = pre.deploy_contract(set_code) + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=1 if invalidity_reason == InvalidityReasons.NONCE else 0, + chain_id=2 + if invalidity_reason == InvalidityReasons.CHAIN_ID + else [0, 1] + if invalidity_reason == InvalidityReasons.MULTIPLE_NONCE + else 0, + signer=signer, + ) + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + signer: Account( + nonce=1, + code=b"", + storage={ + success_slot: 1, + }, + ), + }, + ) From 5bdde65ba580da47cc53612da136e90613bbb5a8 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 26 Jun 2024 00:48:42 +0000 Subject: [PATCH 06/23] new(tests): EIP-7702 --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index aef9f242de..15837e3054 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -652,6 +652,55 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( ) +def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( + state_test: StateTestFiller, + pre: Alloc, +): + """ + Test setting the code of an account with multiple authorization tuples from the same signer + but the first tuple is invalid. + """ + signer = pre.fund_eoa(0) + + success_slot = 1 + + tuple_count = 10 + + addresses = [ + pre.deploy_contract(Op.SSTORE(success_slot, i + 1) + Op.STOP) for i in range(tuple_count) + ] + + tx = Transaction( + gas_limit=1_000_000, + to=signer, + value=0, + authorization_tuples=[ + AuthorizationTuple( + address=address, + nonce=1 if i == 0 else 0, + signer=signer, + ) + for i, address in enumerate(addresses) + ], + sender=pre.fund_eoa(10**18), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + signer: Account( + nonce=1, + code=b"", + storage={ + success_slot: 2, + }, + ), + }, + ) + + @pytest.mark.parametrize( "invalidity_reason", list(InvalidityReasons), From 3697ea29df5056f41af9aceaae0e17b4671b7f92 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 26 Jun 2024 14:14:26 +0000 Subject: [PATCH 07/23] typo --- .../prague/eip7702_eoa_code_tx/test_eoa_code_txs.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 15837e3054..402a0979d2 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -34,7 +34,7 @@ pytestmark = pytest.mark.valid_from("Prague") -class InvalidityReasons(Enum): +class InvalidityReason(Enum): """ Reasons for invalidity. """ @@ -58,7 +58,6 @@ def test_set_code_to_sstore( pre: Alloc, suffix: Bytecode, succeeds: bool, - chain_id: int, ): """ Test the executing a simple SSTORE in a set-code transaction. @@ -703,12 +702,12 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( @pytest.mark.parametrize( "invalidity_reason", - list(InvalidityReasons), + list(InvalidityReason), ) def test_set_code_invalid_authorization_tuple( state_test: StateTestFiller, pre: Alloc, - invalidity_reason: InvalidityReasons, + invalidity_reason: InvalidityReason, ): """ Test attempting to set the code of an account with invalid authorization tuple. @@ -727,11 +726,11 @@ def test_set_code_invalid_authorization_tuple( authorization_tuples=[ AuthorizationTuple( address=set_code_to_address, - nonce=1 if invalidity_reason == InvalidityReasons.NONCE else 0, + nonce=1 if invalidity_reason == InvalidityReason.NONCE else 0, chain_id=2 - if invalidity_reason == InvalidityReasons.CHAIN_ID + if invalidity_reason == InvalidityReason.CHAIN_ID else [0, 1] - if invalidity_reason == InvalidityReasons.MULTIPLE_NONCE + if invalidity_reason == InvalidityReason.MULTIPLE_NONCE else 0, signer=signer, ) From 0f231eb7699665cd28d95f77ad8abbb1d6938e18 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 18:56:41 +0000 Subject: [PATCH 08/23] fix(fw): minor refactor --- src/ethereum_test_types/types.py | 35 ++++++++++++++------------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 536f1c3796..36316a5e9b 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -503,30 +503,25 @@ def to_list(self) -> List[Any]: Uint(self.s), ] - def signing_hash(self) -> Hash: + @cached_property + def signing_bytes(self) -> bytes: """ Returns the data to be signed. """ - return Hash( - keccak256( - int.to_bytes(self.magic, length=1, byteorder="big") - + eth_rlp.encode( - [ - Uint(self.chain_id), - self.address, - self.nonce_list(), - ] - ) - ) + return int.to_bytes(self.magic, length=1, byteorder="big") + eth_rlp.encode( + [ + Uint(self.chain_id), + self.address, + self.nonce_list(), + ] ) def signature(self, private_key: Hash) -> Tuple[int, int, int]: """ Returns the signature of the authorization tuple. """ - signing_hash = self.signing_hash() signature_bytes = PrivateKey(secret=private_key).sign_recoverable( - signing_hash, hasher=None + self.signing_bytes, hasher=keccak256 ) return ( signature_bytes[64], @@ -656,7 +651,7 @@ class Transaction(TransactionGeneric[HexNumber], TransactionTransitionToolConver to: Address | None = Field(Address(0xAA)) data: Bytes = Field(Bytes(b""), alias="input") - authorization_tuples: List[AuthorizationTuple] | None = None + authorization_list: List[AuthorizationTuple] | None = None secret_key: Hash | None = None error: List[TransactionException] | TransactionException | None = Field(None, exclude=True) @@ -705,7 +700,7 @@ def model_post_init(self, __context): if "ty" not in self.model_fields_set: # Try to deduce transaction type from included fields - if self.authorization_tuples is not None: + if self.authorization_list is not None: self.ty = 4 elif self.max_fee_per_blob_gas is not None or self.blob_kzg_commitments is not None: self.ty = 3 @@ -740,8 +735,8 @@ def model_post_init(self, __context): if self.ty == 3 and self.max_fee_per_blob_gas is None: self.max_fee_per_blob_gas = 1 - if self.ty == 4 and self.authorization_tuples is None: - self.authorization_tuples = [] + if self.ty == 4 and self.authorization_list is None: + self.authorization_list = [] if "nonce" not in self.model_fields_set and self.sender is not None: self.nonce = HexNumber(self.sender.get_nonce()) @@ -834,7 +829,7 @@ def signing_envelope(self) -> List[Any]: raise ValueError(f"max_fee_per_gas must be set for type {self.ty} tx") if self.access_list is None: raise ValueError(f"access_list must be set for type {self.ty} tx") - if self.authorization_tuples is None: + if self.authorization_list is None: raise ValueError(f"authorization_tuples must be set for type {self.ty} tx") return [ Uint(self.chain_id), @@ -846,7 +841,7 @@ def signing_envelope(self) -> List[Any]: Uint(self.value), self.data, [a.to_list() for a in self.access_list], - [a.to_list() for a in self.authorization_tuples], + [a.to_list() for a in self.authorization_list], ] elif self.ty == 3: # EIP-4844: https://eips.ethereum.org/EIPS/eip-4844 From 73d81423396c11e715142bdfa56160453955099c Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 18:57:02 +0000 Subject: [PATCH 09/23] new(tests): parametrize sanity test with and without balance --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 402a0979d2..1b011004f9 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -44,6 +44,13 @@ class InvalidityReason(Enum): CHAIN_ID = "chain_id" +@pytest.mark.parametrize( + "eoa_balance", + [ + 0, + 1, + ], +) @pytest.mark.parametrize( "suffix,succeeds", [ @@ -58,12 +65,13 @@ def test_set_code_to_sstore( pre: Alloc, suffix: Bytecode, succeeds: bool, + eoa_balance: int, ): """ Test the executing a simple SSTORE in a set-code transaction. """ storage = Storage() - signer = pre.fund_eoa(0) + signer = pre.fund_eoa(eoa_balance) set_code = ( Op.SSTORE(storage.store_next(1), 1) @@ -79,7 +87,7 @@ def test_set_code_to_sstore( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -115,7 +123,7 @@ def test_set_code_to_self_destruct( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -169,7 +177,7 @@ def test_set_code_to_contract_creator( to=signer, value=0, data=initcode, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -238,7 +246,7 @@ def test_set_code_to_self_caller( gas_limit=1_000_000, to=signer, value=value, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -308,7 +316,7 @@ def test_set_code_to_set_code_caller( gas_limit=1_000_000, to=signer_1, value=value, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address_1, nonce=0, @@ -361,7 +369,7 @@ def test_address_from_set_code( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -431,7 +439,7 @@ def test_ext_code_on_set_code( tx = Transaction( gas_limit=1_000_000, to=signer, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -492,7 +500,7 @@ def test_self_code_on_set_code( tx = Transaction( gas_limit=1_000_000, to=signer, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, @@ -569,7 +577,7 @@ def test_set_code_to_account_deployed_in_same_tx( to=contract_creator_address, value=0, data=initcode, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=deployed_contract_address, nonce=0, @@ -624,7 +632,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=address, nonce=0, @@ -673,7 +681,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=address, nonce=1 if i == 0 else 0, @@ -723,7 +731,7 @@ def test_set_code_invalid_authorization_tuple( gas_limit=1_000_000, to=signer, value=0, - authorization_tuples=[ + authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=1 if invalidity_reason == InvalidityReason.NONCE else 0, From e6130656e778a854bebbf1b89790bab9b9131933 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 19:47:42 +0000 Subject: [PATCH 10/23] fix(tests): fixes --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 112 +++++++++--------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 1b011004f9..acf0d15bc3 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -71,7 +71,7 @@ def test_set_code_to_sstore( Test the executing a simple SSTORE in a set-code transaction. """ storage = Storage() - signer = pre.fund_eoa(eoa_balance) + auth_signer = pre.fund_eoa(eoa_balance) set_code = ( Op.SSTORE(storage.store_next(1), 1) @@ -85,13 +85,13 @@ def test_set_code_to_sstore( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -103,7 +103,7 @@ def test_set_code_to_sstore( tx=tx, post={ set_code_to_address: Account(storage={k: 0 for k in storage}), - signer: Account(nonce=0, code=b"", storage=storage if succeeds else {}), + auth_signer: Account(nonce=0, code=b"", storage=storage if succeeds else {}), }, ) @@ -115,19 +115,19 @@ def test_set_code_to_self_destruct( """ Test the executing self-destruct opcode in a set-code transaction. """ - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) set_code_to_address = pre.deploy_contract(Op.SELFDESTRUCT(Op.ADDRESS)) tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -157,12 +157,12 @@ def test_set_code_to_contract_creator( Test the executing a contract-creating opcode in a set-code transaction. """ storage = Storage() - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) deployed_code = Op.STOP initcode = Initcode(deploy_code=deployed_code) - deployed_contract_address = compute_create_address(signer) + deployed_contract_address = compute_create_address(auth_signer) set_code = Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + Op.SSTORE( storage.store_next(deployed_contract_address), @@ -174,14 +174,14 @@ def test_set_code_to_contract_creator( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, data=initcode, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -193,7 +193,7 @@ def test_set_code_to_contract_creator( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=1, code=b"", storage=storage), + auth_signer: Account(nonce=1, code=b"", storage=storage), deployed_contract_address: Account( code=deployed_code, storage={}, @@ -228,7 +228,7 @@ def test_set_code_to_self_caller( Test the executing a self-call in a set-code transaction. """ storage = Storage() - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) first_entry_slot = storage.store_next(True) re_entry_success_slot = storage.store_next(op != Op.STATICCALL) @@ -236,7 +236,7 @@ def test_set_code_to_self_caller( set_code = Conditional( condition=Op.ISZERO(Op.SLOAD(first_entry_slot)), if_true=Op.SSTORE(first_entry_slot, 1) - + Op.SSTORE(re_entry_call_return_code_slot, op(address=signer, value=value)) + + Op.SSTORE(re_entry_call_return_code_slot, op(address=auth_signer, value=value)) + Op.STOP, if_false=Op.SSTORE(re_entry_success_slot, 1) + Op.STOP, ) @@ -244,13 +244,13 @@ def test_set_code_to_self_caller( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=value, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -262,7 +262,7 @@ def test_set_code_to_self_caller( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=0, code=b"", storage=storage, balance=value), + auth_signer: Account(nonce=0, code=b"", storage=storage, balance=value), }, ) @@ -292,18 +292,18 @@ def test_set_code_to_set_code_caller( """ Test the calling a set-code account from another set-code account. """ - signer_1 = pre.fund_eoa(0) + auth_signer_1 = pre.fund_eoa(0) storage_1 = Storage() set_code_1_call_result_slot = storage_1.store_next(op != Op.STATICCALL) set_code_1_success = storage_1.store_next(True) - signer_2 = pre.fund_eoa(0) + auth_signer_2 = pre.fund_eoa(0) storage_2 = Storage().with_start_slot(storage_1.next_slot()) set_code_2_success = storage_2.store_next(op != Op.STATICCALL) set_code_1 = ( - Op.SSTORE(set_code_1_call_result_slot, op(address=signer_2, value=value)) + Op.SSTORE(set_code_1_call_result_slot, op(address=auth_signer_2, value=value)) + Op.SSTORE(set_code_1_success, 1) + Op.STOP ) @@ -314,18 +314,18 @@ def test_set_code_to_set_code_caller( tx = Transaction( gas_limit=1_000_000, - to=signer_1, + to=auth_signer_1, value=value, authorization_list=[ AuthorizationTuple( address=set_code_to_address_1, nonce=0, - signer=signer_1, + signer=auth_signer_1, ), AuthorizationTuple( address=set_code_to_address_2, nonce=0, - signer=signer_2, + signer=auth_signer_2, ), ], sender=pre.fund_eoa(10**18), @@ -338,12 +338,12 @@ def test_set_code_to_set_code_caller( post={ set_code_to_address_1: Account(storage={k: 0 for k in storage_1}), set_code_to_address_2: Account(storage={k: 0 for k in storage_2}), - signer_1: Account( + auth_signer_1: Account( nonce=0, storage=storage_1 if op != Op.DELEGATECALL else storage_1 + storage_2, balance=0 if op == Op.CALL else value, ), - signer_2: Account( + auth_signer_2: Account( nonce=0, storage=storage_2 if op != Op.DELEGATECALL else {}, balance=value if op == Op.CALL else 0, @@ -360,20 +360,20 @@ def test_address_from_set_code( Test the address opcode in a set-code transaction. """ storage = Storage() - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) - set_code = Op.SSTORE(storage.store_next(signer), Op.ADDRESS) + Op.STOP + set_code = Op.SSTORE(storage.store_next(auth_signer), Op.ADDRESS) + Op.STOP set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -385,7 +385,7 @@ def test_address_from_set_code( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=0, code=b"", storage=storage), + auth_signer: Account(nonce=0, code=b"", storage=storage), }, ) @@ -405,7 +405,7 @@ def test_ext_code_on_set_code( """ Test different ext*code operations on a set-code address. """ - signer = pre.fund_eoa(balance) + auth_signer = pre.fund_eoa(balance) slot = count(1) slot_call_success = next(slot) @@ -427,10 +427,12 @@ def test_ext_code_on_set_code( callee_address = pre.deploy_contract(callee_code) callee_storage = Storage() + auth_signer_storage = Storage() set_code = Op.SSTORE(slot_call_success, Op.CALL(address=callee_address)) + Op.STOP + auth_signer_storage[slot_call_success] = True set_code_to_address = pre.deploy_contract(set_code) - callee_storage[slot_caller] = signer + callee_storage[slot_caller] = auth_signer callee_storage[slot_ext_code_size_result] = len(set_code) callee_storage[slot_ext_code_hash_result] = set_code.keccak256() callee_storage[slot_ext_code_copy_result] = bytes(set_code).ljust(32, b"\x00")[:32] @@ -438,12 +440,12 @@ def test_ext_code_on_set_code( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -455,7 +457,7 @@ def test_ext_code_on_set_code( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=0, code=b"", storage={}, balance=balance), + auth_signer: Account(nonce=0, code=b"", storage=auth_signer_storage, balance=balance), callee_address: Account(storage=callee_storage), }, ) @@ -476,7 +478,7 @@ def test_self_code_on_set_code( """ Test codesize and codecopy operations on a set-code address. """ - signer = pre.fund_eoa(balance) + auth_signer = pre.fund_eoa(balance) slot = count(1) slot_code_size_result = next(slot) @@ -499,12 +501,12 @@ def test_self_code_on_set_code( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, authorization_list=[ AuthorizationTuple( address=set_code_to_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -516,7 +518,7 @@ def test_self_code_on_set_code( tx=tx, post={ set_code_to_address: Account(storage={}), - signer: Account(nonce=0, code=b"", storage={}, balance=balance), + auth_signer: Account(nonce=0, code=b"", storage=storage, balance=balance), }, ) @@ -537,7 +539,7 @@ def test_set_code_to_account_deployed_in_same_tx( Test setting the code of an account to an address that is deployed in the same transaction, and test calling the set-code address and the deployed contract. """ - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) success_slot = 1 @@ -551,7 +553,7 @@ def test_set_code_to_account_deployed_in_same_tx( contract_creator_code = ( Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + Op.SSTORE(deployed_contract_address_slot, create_op(offset=0, size=Op.CALLDATASIZE)) - + Op.SSTORE(signer_call_return_code_slot, Op.CALL(address=signer)) + + Op.SSTORE(signer_call_return_code_slot, Op.CALL(address=auth_signer)) + Op.SSTORE( deployed_contract_call_return_code_slot, Op.CALL(address=Op.SLOAD(deployed_contract_address_slot)), @@ -581,7 +583,7 @@ def test_set_code_to_account_deployed_in_same_tx( AuthorizationTuple( address=deployed_contract_address, nonce=0, - signer=signer, + signer=auth_signer, ), ], sender=pre.fund_eoa(10**18), @@ -595,7 +597,7 @@ def test_set_code_to_account_deployed_in_same_tx( deployed_contract_address: Account( storage={success_slot: 1}, ), - signer: Account( + auth_signer: Account( nonce=0, code=b"", storage={}, @@ -618,7 +620,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( """ Test setting the code of an account with multiple authorization tuples from the same signer. """ - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) success_slot = 1 @@ -630,13 +632,13 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( address=address, nonce=0, - signer=signer, + signer=auth_signer, ) for address in addresses ], @@ -648,7 +650,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( pre=pre, tx=tx, post={ - signer: Account( + auth_signer: Account( nonce=1, code=b"", storage={ @@ -667,7 +669,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( Test setting the code of an account with multiple authorization tuples from the same signer but the first tuple is invalid. """ - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) success_slot = 1 @@ -679,13 +681,13 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( address=address, nonce=1 if i == 0 else 0, - signer=signer, + signer=auth_signer, ) for i, address in enumerate(addresses) ], @@ -697,7 +699,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( pre=pre, tx=tx, post={ - signer: Account( + auth_signer: Account( nonce=1, code=b"", storage={ @@ -720,7 +722,7 @@ def test_set_code_invalid_authorization_tuple( """ Test attempting to set the code of an account with invalid authorization tuple. """ - signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(0) success_slot = 1 @@ -729,7 +731,7 @@ def test_set_code_invalid_authorization_tuple( tx = Transaction( gas_limit=1_000_000, - to=signer, + to=auth_signer, value=0, authorization_list=[ AuthorizationTuple( @@ -740,7 +742,7 @@ def test_set_code_invalid_authorization_tuple( else [0, 1] if invalidity_reason == InvalidityReason.MULTIPLE_NONCE else 0, - signer=signer, + signer=auth_signer, ) ], sender=pre.fund_eoa(10**18), @@ -751,7 +753,7 @@ def test_set_code_invalid_authorization_tuple( pre=pre, tx=tx, post={ - signer: Account( + auth_signer: Account( nonce=1, code=b"", storage={ From c1f5909b7e5d828062f0c8faf0a856c052e8082a Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 22:16:50 +0000 Subject: [PATCH 11/23] fix(fw): nonce field in the auth tuple --- src/ethereum_test_types/types.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 36316a5e9b..32228ef12c 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -464,7 +464,7 @@ class AuthorizationTupleGeneric(CamelModel, Generic[NumberBoundTypeVar]): chain_id: NumberBoundTypeVar = Field(1) # type: ignore address: Address - nonce: List[NumberBoundTypeVar] | NumberBoundTypeVar | None = None + nonce: List[NumberBoundTypeVar] = Field(default_factory=list) v: NumberBoundTypeVar = Field(0) # type: ignore r: NumberBoundTypeVar = Field(0) # type: ignore @@ -472,23 +472,15 @@ class AuthorizationTupleGeneric(CamelModel, Generic[NumberBoundTypeVar]): magic: ClassVar[int] = 0x05 - def model_post_init(self, __context: Any) -> None: + @model_validator(mode="before") + @classmethod + def convert_nonce_information(cls, data: Any) -> Any: """ Automatically converts the nonce to a list if it is not already. """ - super().model_post_init(__context) - if self.nonce is not None and not isinstance(self.nonce, list): - self.nonce = [self.nonce] - - def nonce_list(self) -> List[Uint]: - """ - Returns the nonce as a list. - """ - if self.nonce is None: - return [] - return ( - [Uint(n) for n in self.nonce] if isinstance(self.nonce, list) else [Uint(self.nonce)] - ) + if "nonce" in data and not isinstance(data["nonce"], list): + data["nonce"] = [data["nonce"]] + return data def to_list(self) -> List[Any]: """ @@ -497,7 +489,7 @@ def to_list(self) -> List[Any]: return [ Uint(self.chain_id), self.address, - self.nonce_list(), + [Uint(n) for n in self.nonce], Uint(self.v), Uint(self.r), Uint(self.s), @@ -512,7 +504,7 @@ def signing_bytes(self) -> bytes: [ Uint(self.chain_id), self.address, - self.nonce_list(), + [Uint(n) for n in self.nonce], ] ) From 232cd21f747d626e39bc168210b17307be81425b Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 22:17:36 +0000 Subject: [PATCH 12/23] fix(tests): fix some of the tests --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index acf0d15bc3..9d5e1ff2a6 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -162,15 +162,20 @@ def test_set_code_to_contract_creator( deployed_code = Op.STOP initcode = Initcode(deploy_code=deployed_code) - deployed_contract_address = compute_create_address(auth_signer) + if op == Op.CREATE: + deployed_contract_address = compute_create_address(auth_signer) + elif op == Op.CREATE2: + deployed_contract_address = compute_create2_address( + address=auth_signer, + salt=0, + initcode=initcode, + ) set_code = Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + Op.SSTORE( storage.store_next(deployed_contract_address), op(value=0, offset=0, size=Op.CALLDATASIZE), ) - set_code_to_address = pre.deploy_contract( - set_code, - ) + set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( gas_limit=1_000_000, @@ -651,7 +656,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( tx=tx, post={ auth_signer: Account( - nonce=1, + nonce=0, code=b"", storage={ success_slot: 1, @@ -700,7 +705,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( tx=tx, post={ auth_signer: Account( - nonce=1, + nonce=0, code=b"", storage={ success_slot: 2, @@ -736,12 +741,12 @@ def test_set_code_invalid_authorization_tuple( authorization_list=[ AuthorizationTuple( address=set_code_to_address, - nonce=1 if invalidity_reason == InvalidityReason.NONCE else 0, - chain_id=2 - if invalidity_reason == InvalidityReason.CHAIN_ID + nonce=1 + if invalidity_reason == InvalidityReason.NONCE else [0, 1] if invalidity_reason == InvalidityReason.MULTIPLE_NONCE else 0, + chain_id=2 if invalidity_reason == InvalidityReason.CHAIN_ID else 0, signer=auth_signer, ) ], @@ -754,10 +759,10 @@ def test_set_code_invalid_authorization_tuple( tx=tx, post={ auth_signer: Account( - nonce=1, + nonce=0, code=b"", storage={ - success_slot: 1, + success_slot: 0, }, ), }, From 25c37418a9dd196e28f213e8eb78db7d2c870de6 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Thu, 27 Jun 2024 23:36:57 +0000 Subject: [PATCH 13/23] fix(tests): test fixes and skips --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 86 +++++++++++-------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 9d5e1ff2a6..45c7f2409e 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -33,6 +33,9 @@ pytestmark = pytest.mark.valid_from("Prague") +# FIXME: Temporary workaround, should be zero +auth_account_start_balance = 1 + class InvalidityReason(Enum): """ @@ -47,8 +50,8 @@ class InvalidityReason(Enum): @pytest.mark.parametrize( "eoa_balance", [ - 0, - 1, + pytest.param(0, marks=pytest.mark.xfail(reason="evm fails on zero balance")), + pytest.param(1), ], ) @pytest.mark.parametrize( @@ -84,7 +87,7 @@ def test_set_code_to_sstore( ) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ @@ -115,12 +118,12 @@ def test_set_code_to_self_destruct( """ Test the executing self-destruct opcode in a set-code transaction. """ - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) set_code_to_address = pre.deploy_contract(Op.SELFDESTRUCT(Op.ADDRESS)) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ @@ -157,7 +160,7 @@ def test_set_code_to_contract_creator( Test the executing a contract-creating opcode in a set-code transaction. """ storage = Storage() - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) deployed_code = Op.STOP initcode = Initcode(deploy_code=deployed_code) @@ -178,7 +181,7 @@ def test_set_code_to_contract_creator( set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, data=initcode, @@ -233,7 +236,7 @@ def test_set_code_to_self_caller( Test the executing a self-call in a set-code transaction. """ storage = Storage() - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) first_entry_slot = storage.store_next(True) re_entry_success_slot = storage.store_next(op != Op.STATICCALL) @@ -248,7 +251,7 @@ def test_set_code_to_self_caller( set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=value, authorization_list=[ @@ -258,7 +261,7 @@ def test_set_code_to_self_caller( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(10**21), ) state_test( @@ -267,7 +270,12 @@ def test_set_code_to_self_caller( tx=tx, post={ set_code_to_address: Account(storage={}), - auth_signer: Account(nonce=0, code=b"", storage=storage, balance=value), + auth_signer: Account( + nonce=0, + code=b"", + storage=storage, + balance=auth_account_start_balance + value, + ), }, ) @@ -288,7 +296,7 @@ def test_set_code_to_self_caller( 10**18, ], ) -def test_set_code_to_set_code_caller( +def test_set_code_call_set_code( state_test: StateTestFiller, pre: Alloc, op: Op, @@ -297,13 +305,13 @@ def test_set_code_to_set_code_caller( """ Test the calling a set-code account from another set-code account. """ - auth_signer_1 = pre.fund_eoa(0) + auth_signer_1 = pre.fund_eoa(auth_account_start_balance) storage_1 = Storage() set_code_1_call_result_slot = storage_1.store_next(op != Op.STATICCALL) set_code_1_success = storage_1.store_next(True) - auth_signer_2 = pre.fund_eoa(0) + auth_signer_2 = pre.fund_eoa(auth_account_start_balance) storage_2 = Storage().with_start_slot(storage_1.next_slot()) set_code_2_success = storage_2.store_next(op != Op.STATICCALL) @@ -318,7 +326,7 @@ def test_set_code_to_set_code_caller( set_code_to_address_2 = pre.deploy_contract(set_code_2) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer_1, value=value, authorization_list=[ @@ -333,7 +341,7 @@ def test_set_code_to_set_code_caller( signer=auth_signer_2, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(10**21), ) state_test( @@ -345,13 +353,13 @@ def test_set_code_to_set_code_caller( set_code_to_address_2: Account(storage={k: 0 for k in storage_2}), auth_signer_1: Account( nonce=0, - storage=storage_1 if op != Op.DELEGATECALL else storage_1 + storage_2, - balance=0 if op == Op.CALL else value, + storage=storage_1 if op in [Op.CALL, Op.STATICCALL] else storage_1 + storage_2, + balance=(0 if op == Op.CALL else value) + auth_account_start_balance, ), auth_signer_2: Account( nonce=0, - storage=storage_2 if op != Op.DELEGATECALL else {}, - balance=value if op == Op.CALL else 0, + storage=storage_2 if op == Op.CALL else {}, + balance=(value if op == Op.CALL else 0) + auth_account_start_balance, ), }, ) @@ -365,13 +373,13 @@ def test_address_from_set_code( Test the address opcode in a set-code transaction. """ storage = Storage() - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) set_code = Op.SSTORE(storage.store_next(auth_signer), Op.ADDRESS) + Op.STOP set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ @@ -398,8 +406,8 @@ def test_address_from_set_code( @pytest.mark.parametrize( "balance", [ - 0, - 10**18, + pytest.param(0, marks=pytest.mark.xfail(reason="evm fails on zero balance")), + pytest.param(10**18), ], ) def test_ext_code_on_set_code( @@ -444,7 +452,7 @@ def test_ext_code_on_set_code( callee_storage[slot_ext_balance_result] = balance tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, authorization_list=[ AuthorizationTuple( @@ -471,8 +479,8 @@ def test_ext_code_on_set_code( @pytest.mark.parametrize( "balance", [ - 0, - 10**18, + pytest.param(0, marks=pytest.mark.xfail(reason="evm fails on zero balance")), + pytest.param(10**18), ], ) def test_self_code_on_set_code( @@ -505,7 +513,7 @@ def test_self_code_on_set_code( storage[slot_self_balance_result] = balance tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, authorization_list=[ AuthorizationTuple( @@ -544,7 +552,7 @@ def test_set_code_to_account_deployed_in_same_tx( Test setting the code of an account to an address that is deployed in the same transaction, and test calling the set-code address and the deployed contract. """ - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) success_slot = 1 @@ -580,7 +588,7 @@ def test_set_code_to_account_deployed_in_same_tx( ) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=contract_creator_address, value=0, data=initcode, @@ -625,7 +633,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( """ Test setting the code of an account with multiple authorization tuples from the same signer. """ - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) success_slot = 1 @@ -636,7 +644,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( ] tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ @@ -674,7 +682,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( Test setting the code of an account with multiple authorization tuples from the same signer but the first tuple is invalid. """ - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) success_slot = 1 @@ -685,7 +693,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( ] tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ @@ -717,7 +725,11 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( @pytest.mark.parametrize( "invalidity_reason", - list(InvalidityReason), + [ + InvalidityReason.NONCE, + InvalidityReason.MULTIPLE_NONCE, + pytest.param(InvalidityReason.CHAIN_ID, marks=pytest.mark.xfail(reason="evm issue")), + ], ) def test_set_code_invalid_authorization_tuple( state_test: StateTestFiller, @@ -727,7 +739,7 @@ def test_set_code_invalid_authorization_tuple( """ Test attempting to set the code of an account with invalid authorization tuple. """ - auth_signer = pre.fund_eoa(0) + auth_signer = pre.fund_eoa(auth_account_start_balance) success_slot = 1 @@ -735,7 +747,7 @@ def test_set_code_invalid_authorization_tuple( set_code_to_address = pre.deploy_contract(set_code) tx = Transaction( - gas_limit=1_000_000, + gas_limit=10_000_000, to=auth_signer, value=0, authorization_list=[ From 25c82ab8a3398abeb59bc892f3e1186cc7aa743c Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Fri, 28 Jun 2024 05:12:20 +0000 Subject: [PATCH 14/23] fix(test): skip --- tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 45c7f2409e..bf80899c4d 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -727,7 +727,9 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( "invalidity_reason", [ InvalidityReason.NONCE, - InvalidityReason.MULTIPLE_NONCE, + pytest.param( + InvalidityReason.MULTIPLE_NONCE, marks=pytest.mark.xfail(reason="test issue") + ), pytest.param(InvalidityReason.CHAIN_ID, marks=pytest.mark.xfail(reason="evm issue")), ], ) From 4ff3adda68eef2ff223863f04c576312a4aea92d Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Mon, 8 Jul 2024 18:10:15 +0000 Subject: [PATCH 15/23] fix(specs): output the authorization list to the fixture --- src/ethereum_test_fixtures/blockchain.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ethereum_test_fixtures/blockchain.py b/src/ethereum_test_fixtures/blockchain.py index 01dd6d787d..2133cbecf5 100644 --- a/src/ethereum_test_fixtures/blockchain.py +++ b/src/ethereum_test_fixtures/blockchain.py @@ -327,6 +327,8 @@ class FixtureAuthorizationTuple(AuthorizationTupleGeneric[ZeroPaddedHexNumber]): Authorization tuple for fixture transactions. """ + signer: Address | None = None + @classmethod def from_authorization_tuple( cls, auth_tuple: AuthorizationTupleGeneric @@ -342,7 +344,7 @@ class FixtureTransaction(TransactionFixtureConverter, TransactionGeneric[ZeroPad Representation of an Ethereum transaction within a test Fixture. """ - authorization_tuples: List[FixtureAuthorizationTuple] | None = None + authorization_list: List[FixtureAuthorizationTuple] | None = None @classmethod def from_transaction(cls, tx: Transaction) -> "FixtureTransaction": From 0658d1f4275d4dd4065d74cf30299f639f4a0f3b Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 9 Jul 2024 17:35:46 +0000 Subject: [PATCH 16/23] nit --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index bf80899c4d..9506dccd16 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -97,7 +97,7 @@ def test_set_code_to_sstore( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -133,7 +133,7 @@ def test_set_code_to_self_destruct( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -192,7 +192,7 @@ def test_set_code_to_contract_creator( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -389,7 +389,7 @@ def test_address_from_set_code( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -461,7 +461,7 @@ def test_ext_code_on_set_code( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -522,7 +522,7 @@ def test_self_code_on_set_code( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -599,7 +599,7 @@ def test_set_code_to_account_deployed_in_same_tx( signer=auth_signer, ), ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -655,7 +655,7 @@ def test_set_code_multiple_valid_authorization_tuples_same_signer( ) for address in addresses ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -704,7 +704,7 @@ def test_set_code_multiple_valid_authorization_tuples_first_invalid_same_signer( ) for i, address in enumerate(addresses) ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( @@ -764,7 +764,7 @@ def test_set_code_invalid_authorization_tuple( signer=auth_signer, ) ], - sender=pre.fund_eoa(10**18), + sender=pre.fund_eoa(), ) state_test( From 1fb3d02d09ed2087645207c12a1f6d6d3a2b4695 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 9 Jul 2024 19:50:44 +0000 Subject: [PATCH 17/23] more tests --- .../eip7702_eoa_code_tx/test_eoa_code_txs.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 9506dccd16..8c633eccd6 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -12,6 +12,8 @@ Account, Alloc, AuthorizationTuple, + Block, + BlockchainTestFiller, Bytecode, Conditional, Environment, @@ -111,6 +113,137 @@ def test_set_code_to_sstore( ) +def test_set_code_to_sstore_then_sload( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +): + """ + Test the executing a simple SSTORE then SLOAD in two separate set-code transactions. + """ + auth_signer = pre.fund_eoa(auth_account_start_balance) + sender = pre.fund_eoa() + + storage_key_1 = 0x1 + storage_key_2 = 0x2 + storage_value = 0x1234 + + set_code_1 = Op.SSTORE(storage_key_1, storage_value) + Op.STOP + set_code_1_address = pre.deploy_contract(set_code_1) + + set_code_2 = Op.SSTORE(storage_key_2, Op.ADD(Op.SLOAD(storage_key_1), 1)) + Op.STOP + set_code_2_address = pre.deploy_contract(set_code_2) + + tx_1 = Transaction( + gas_limit=50_000, + to=auth_signer, + value=0, + authorization_list=[ + AuthorizationTuple( + address=set_code_1_address, + nonce=0, + signer=auth_signer, + ), + ], + sender=sender, + ) + + tx_2 = Transaction( + gas_limit=50_000, + to=auth_signer, + value=0, + authorization_list=[ + AuthorizationTuple( + address=set_code_2_address, + nonce=0, + signer=auth_signer, + ), + ], + sender=sender, + ) + + block = Block( + txs=[tx_1, tx_2], + ) + + blockchain_test( + pre=pre, + post={ + auth_signer: Account( + nonce=0, + code=b"", + storage={ + storage_key_1: storage_value, + storage_key_2: storage_value + 1, + }, + ), + }, + blocks=[block], + ) + + +@pytest.mark.parametrize( + "call_opcode", + [ + Op.CALL, + Op.DELEGATECALL, + Op.STATICCALL, + Op.CALLCODE, + ], +) +@pytest.mark.parametrize( + "return_opcode", + [ + Op.RETURN, + Op.REVERT, + ], +) +def test_set_code_to_tstore_reentry( + state_test: StateTestFiller, + pre: Alloc, + call_opcode: Op, + return_opcode: Op, +): + """ + Test the executing a simple TSTORE in a set-code transaction, which also performs a + re-entry to TLOAD the value. + """ + auth_signer = pre.fund_eoa(auth_account_start_balance) + + tload_value = 0x1234 + set_code = Conditional( + condition=Op.ISZERO(Op.TLOAD(1)), + if_true=Op.TSTORE(1, tload_value) + + call_opcode(address=Op.ADDRESS) + + Op.RETURNDATACOPY(0, 0, 32) + + Op.SSTORE(2, Op.MLOAD(0)), + if_false=Op.MSTORE(0, Op.TLOAD(1)) + return_opcode(size=32), + ) + set_code_to_address = pre.deploy_contract(set_code) + + tx = Transaction( + gas_limit=100_000, + to=auth_signer, + value=0, + authorization_list=[ + AuthorizationTuple( + address=set_code_to_address, + nonce=0, + signer=auth_signer, + ), + ], + sender=pre.fund_eoa(), + ) + + state_test( + env=Environment(), + pre=pre, + tx=tx, + post={ + auth_signer: Account(nonce=0, code=b"", storage={2: tload_value}), + }, + ) + + def test_set_code_to_self_destruct( state_test: StateTestFiller, pre: Alloc, From 382586a0527f43882a4de6618f395369f0743f8d Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 10 Jul 2024 23:21:46 +0000 Subject: [PATCH 18/23] fix(fw): State test type-4 fix --- src/ethereum_test_fixtures/state.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ethereum_test_fixtures/state.py b/src/ethereum_test_fixtures/state.py index 8519d65339..7cf26fde8f 100644 --- a/src/ethereum_test_fixtures/state.py +++ b/src/ethereum_test_fixtures/state.py @@ -34,6 +34,8 @@ class FixtureAuthorizationTuple(AuthorizationTupleGeneric[ZeroPaddedHexNumber]): Authorization tuple for fixture transactions. """ + signer: Address | None = None + @classmethod def from_authorization_tuple( cls, auth_tuple: AuthorizationTupleGeneric @@ -58,7 +60,7 @@ class FixtureTransaction(TransactionFixtureConverter): value: List[ZeroPaddedHexNumber] data: List[Bytes] access_lists: List[List[AccessList]] | None = None - authorization_tuples: List[FixtureAuthorizationTuple] | None = None + authorization_list: List[FixtureAuthorizationTuple] | None = None max_fee_per_blob_gas: ZeroPaddedHexNumber | None = None blob_versioned_hashes: Sequence[Hash] | None = None sender: Address | None = None From 5014a06cf199489a5856396e4d93b74de064b0e2 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 23 Jul 2024 17:42:35 +0000 Subject: [PATCH 19/23] fix(fw): Transaction type checks --- src/ethereum_test_types/types.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ethereum_test_types/types.py b/src/ethereum_test_types/types.py index 32228ef12c..5474ee4ac2 100644 --- a/src/ethereum_test_types/types.py +++ b/src/ethereum_test_types/types.py @@ -718,17 +718,27 @@ def model_post_init(self, __context): self.gas_price = 10 if self.ty >= 1 and self.access_list is None: self.access_list = [] + if self.ty < 1: + assert self.access_list is None, "access_list must be None" if self.ty >= 2 and self.max_fee_per_gas is None: self.max_fee_per_gas = 7 if self.ty >= 2 and self.max_priority_fee_per_gas is None: self.max_priority_fee_per_gas = 0 + if self.ty < 2: + assert self.max_fee_per_gas is None, "max_fee_per_gas must be None" + assert self.max_priority_fee_per_gas is None, "max_priority_fee_per_gas must be None" if self.ty == 3 and self.max_fee_per_blob_gas is None: self.max_fee_per_blob_gas = 1 + if self.ty != 3: + assert self.blob_versioned_hashes is None, "blob_versioned_hashes must be None" + assert self.max_fee_per_blob_gas is None, "max_fee_per_blob_gas must be None" if self.ty == 4 and self.authorization_list is None: self.authorization_list = [] + if self.ty != 4: + assert self.authorization_list is None, "authorization_list must be None" if "nonce" not in self.model_fields_set and self.sender is not None: self.nonce = HexNumber(self.sender.get_nonce()) From f1bf0423fb9ac33171929ea353b40bf2ff68fc87 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 23 Jul 2024 17:44:23 +0000 Subject: [PATCH 20/23] fix(fixtures): Fixture transaciton type-4 test --- .../tests/test_blockchain.py | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/ethereum_test_fixtures/tests/test_blockchain.py b/src/ethereum_test_fixtures/tests/test_blockchain.py index 6a03decc39..a5fa642b48 100644 --- a/src/ethereum_test_fixtures/tests/test_blockchain.py +++ b/src/ethereum_test_fixtures/tests/test_blockchain.py @@ -15,12 +15,14 @@ Bytes, Hash, HeaderNonce, + TestPrivateKey, ZeroPaddedHexNumber, to_json, ) from ethereum_test_exceptions import BlockException, EngineAPIError, TransactionException from ethereum_test_forks import Prague from ethereum_test_types import ( + EOA, AccessList, AuthorizationTuple, ConsolidationRequest, @@ -33,7 +35,6 @@ from ..blockchain import ( EngineNewPayloadParameters, - FixtureAuthorizationTuple, FixtureBlockBase, FixtureEngineNewPayload, FixtureExecutionPayload, @@ -180,6 +181,51 @@ }, id="fixture_transaction_type_3_default_values", ), + pytest.param( + True, + FixtureTransaction.from_transaction( + Transaction( + ty=4, + max_fee_per_gas=7, + authorization_list=[ + AuthorizationTuple( + chain_id=1, + address=2, + nonce=[3], + signer=EOA(key=TestPrivateKey), + ) + ], + ).with_signature_and_sender() + ), + { + "type": "0x04", + "chainId": "0x01", + "nonce": "0x00", + "to": "0x00000000000000000000000000000000000000aa", + "value": "0x00", + "data": "0x", + "gasLimit": "0x5208", + "maxPriorityFeePerGas": "0x00", + "maxFeePerGas": "0x07", + "accessList": [], + "authorizationList": [ + { + "chainId": "0x01", + "address": Address(2).hex(), + "nonce": ["0x03"], + "v": "0x00", + "r": "0x796b0a59fe796b5aab79259988f4b18bb7966dc9aa0a01d226859057f539d8f6", + "s": "0x7456ad9b8b4e157d8a150ae7d568bb93e668bf1d5970756f7fe7b7f2472235fe", + "signer": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + } + ], + "v": "0x01", + "r": "0xb9f3ad929ffdb846cbe357fa25e6ab93cc6e10e76da170a12baf03f8a34ba141", + "s": "0x04992060cfa252f5ac18ac1ccb340a821497d50812a225646094d2ad08b8eeaa", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + }, + id="fixture_transaction_type_4", + ), pytest.param( True, FixtureTransaction.from_transaction( From 2c0abb9808a57b4e0c442ce4803be7d89c00ef02 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 23 Jul 2024 17:44:39 +0000 Subject: [PATCH 21/23] fix(base_types): Storage methods --- .../composite_types.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/ethereum_test_base_types/composite_types.py b/src/ethereum_test_base_types/composite_types.py index 208e8e3575..7cd17f8736 100644 --- a/src/ethereum_test_base_types/composite_types.py +++ b/src/ethereum_test_base_types/composite_types.py @@ -2,10 +2,9 @@ Base composite types for Ethereum test cases. """ from dataclasses import dataclass -from itertools import count -from typing import Any, ClassVar, Dict, Iterator, SupportsBytes, Type, TypeAlias +from typing import Any, ClassVar, Dict, SupportsBytes, Type, TypeAlias -from pydantic import Field, RootModel, TypeAdapter +from pydantic import Field, PrivateAttr, RootModel, TypeAdapter from .base_types import Address, Bytes, Hash, HashInt, HexNumber, ZeroPaddedHexNumber from .conversions import BytesConvertible, NumberConvertible @@ -24,7 +23,7 @@ class Storage(RootModel[Dict[StorageKeyValueType, StorageKeyValueType]]): root: Dict[StorageKeyValueType, StorageKeyValueType] = Field(default_factory=dict) - _current_slot: Iterator[int] = count(0) + _current_slot: int = PrivateAttr(0) StorageDictType: ClassVar[TypeAlias] = Dict[ str | int | bytes | SupportsBytes, str | int | bytes | SupportsBytes @@ -161,10 +160,23 @@ def __bool__(self) -> bool: """Returns True if the storage is not empty""" return any(v for v in self.root.values()) + def __add__(self, other: "Storage") -> "Storage": + """ + Returns a new storage that is the sum of two storages. + """ + return Storage({**self.root, **other.root}) + def keys(self) -> set[StorageKeyValueType]: """Returns the keys of the storage""" return set(self.root.keys()) + def set_next_slot(self, slot: int) -> "Storage": + """ + Sets the next slot to be used by `store_next`. + """ + self._current_slot = slot + return self + def store_next( self, value: StorageKeyValueTypeConvertible | StorageKeyValueType | bool ) -> StorageKeyValueType: @@ -174,10 +186,17 @@ def store_next( Increments the key counter so the next time this function is called, the next key is used. """ - slot = StorageKeyValueTypeAdapter.validate_python(next(self._current_slot)) + slot = StorageKeyValueTypeAdapter.validate_python(self._current_slot) + self._current_slot += 1 self[slot] = StorageKeyValueTypeAdapter.validate_python(value) return slot + def peek_slot(self) -> int: + """ + Peeks the next slot that will be used by `store_next`. + """ + return self._current_slot + def contains(self, other: "Storage") -> bool: """ Returns True if self contains all keys with equal value as From a475721a034d1b4624458995e1607f5db433c8a4 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 23 Jul 2024 17:44:53 +0000 Subject: [PATCH 22/23] fix(tests): storage method usage --- tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py index 8c633eccd6..3264fe90ca 100644 --- a/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py +++ b/tests/prague/eip7702_eoa_code_tx/test_eoa_code_txs.py @@ -445,7 +445,7 @@ def test_set_code_call_set_code( set_code_1_success = storage_1.store_next(True) auth_signer_2 = pre.fund_eoa(auth_account_start_balance) - storage_2 = Storage().with_start_slot(storage_1.next_slot()) + storage_2 = Storage().set_next_slot(storage_1.peek_slot()) set_code_2_success = storage_2.store_next(op != Op.STATICCALL) set_code_1 = ( From 776dd78ed257e389225f0889c2401504f8367766 Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Tue, 23 Jul 2024 17:53:58 +0000 Subject: [PATCH 23/23] changelog --- docs/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c92f28f60b..ca3d40f1d7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,6 +29,7 @@ Test fixtures for use by clients are available for each release on the [Github r - ✨ Add tests for subcontainer kind validation from [EIP-7620: EOF Contract Creation](https://eips.ethereum.org/EIPS/eip-7620) for the cases with deeply nested containers and non-first code sections ([#676](https://github.com/ethereum/execution-spec-tests/pull/676)). - ✨ Add tests for runtime stack overflow at CALLF instruction from [EIP-4750: EOF - Functions](https://eips.ethereum.org/EIPS/eip-4750) ([#678](https://github.com/ethereum/execution-spec-tests/pull/678)). - ✨ Add tests for runtime stack overflow at JUMPF instruction from [EIP-6206: EOF - JUMPF and non-returning functions](https://eips.ethereum.org/EIPS/eip-6206) ([#690](https://github.com/ethereum/execution-spec-tests/pull/690)). +- ✨ Add tests for Devnet-1 version of [EIP-7702: Set EOA account code](https://eips.ethereum.org/EIPS/eip-7702) ([#621](https://github.com/ethereum/execution-spec-tests/pull/621)) ### 🛠️ Framework