Skip to content

Commit 12850ce

Browse files
sonhv0212spencer-tbgumb0chfastmarioevz
authored
new(tests): sync prague tests (#32)
* feat(tests): EIP-2537: add bls12 audit test cases (ethereum#1275) * fix(test): remove test violating EIP-7523 (ethereum#1257) * fix(test): remove test violating EIP-7523 * Simplify the test by removing parameterization * chore(tests): update bls12 test vectors (ethereum#1289) * new(tests): add state test with empty 7702 authorization list (ethereum#1224) * new(tests): add state test with empty 7702 authorization list * fix(clis): Add exception to EELS --------- Co-authored-by: Mario Vega <[email protected]> * new(tests): EIP-7702: Sender not EOA test * Add self-sponsored variant * apply comments * fix(test): improve EIP-7702 test for type 4 transaction with empty to (ethereum#1337) * fix(tests): EIP-7702 verification/merge issue (ethereum#1358) * new(tests): EIP-7702: More RLP tests (ethereum#1347) * refactor(types): remove nonce-as-list from auth list * new(tests): EIP-7702: Tx serialization tests * refactor(tests): Use RLP encoding changes * Change log * new(tests): EIP-7702: more rlp tests * 🧹 chore(EIP-7623): Remove duplicate fixture (ethereum#1381) Co-authored-by: raxhvl <[email protected]> * feat(tests): Additional EIP-2935 coverage (ethereum#1379) Update CHANGELOG.md * new(tests): EIP-7702: Delegation Designation as Initcode, max-fee-per-gas tests (ethereum#1372) * new(tests): EIP-7702: Delegation designation as initcode * new(tests): EIP-7702: Invalid max_fee_per_gas/max_priority_fee_per_gas * docs: changelog * refactor(tests): Combine similar cases into one Co-Authored-By: spencer <[email protected]> --------- Co-authored-by: spencer <[email protected]> * fix(execute,tests): EIP-7702: send tx of eoa after setcode tx is mined (ethereum#1411) * fix(tests): EIP-7702: send transaction of an EOA after setcode tx is mined * refactor(execute): send batch of txs by block * fix(tests): Parametrize instead of change * refactor(execution): Change error check to per-block basis --------- Co-authored-by: Mario Vega <[email protected]> * feat(tests): update 7702 precompile test cases (ethereum#1431) - Add a test case that calls the delegated precompile acct directly from the same transaction as the auth. Supply enough gas only to cover the intrinsic cost of the call, with no extra gas for any precompile code execution. - Update the test case for all call opcodes to use all of the call opcodes. * feat(tests): eip7623 - parametrize `test_transaction_validity_type_*` with a tx to an eoa (ethereum#1422) * feat(tests): EIP-2537: extra BLS12-381 coverage (ethereum#1350) * feat(tests): bls point generator and extra coverage. * chore(tests): remove bls msm duplicates. * chore: fix tox issues. * feat(tests): Add more msm invalid tests --------- Co-authored-by: Mario Vega <[email protected]> * new(tests): EIP-7702 - ensure DELEGATECALLing a 7702 target works (ethereum#1485) Co-authored-by: Mario Vega <[email protected]> * feat(tests): add bls map to curve on identity cases (ethereum#1505) * fix(tests): EIP-7702: use penultimate block number instead of fixing block 0 (ethereum#1390) * fix(tests): EIP-7702: use penultimate block number instead of fixing block 0 * fix(tests): EIP-7702: use contract code to get blocknumber instead of rpc * Update uv.lock * Update uv.lock * Update uv.lock * Update uv.lock * Update pyproject.toml * refactor(base_types,types,tests): Create RLP Serialization Classes (ethereum#1359) * fix(types): tests: Add serialization unit tests * fix(types): tests: Fix unit tests * feat(base_types): Implement serialization RLP types * feat(base_types,types,fixtures): Apply serialization RLP types * fix(types): Auth tuple defaults * fix(fixtures,rpc,specs,types,tests): Transaction.rlp is a function * fix(base_types,types,rpc): fixes * refactor(base_types,types): Network wrapped transaction * fix(tests): Network wrapped transactions * fix(types): Fix NetworkWrappedTransaction rlp prefix * docs: Changelog * Apply suggestions from code review Co-authored-by: spencer <[email protected]> * Add network wrapper explanation * fix: tox * Update docs/CHANGELOG.md Co-authored-by: spencer <[email protected]> * Add comment --------- Co-Authored-By: spencer <[email protected]> * fix(tests): EIP-2537: mark slow * fix(tests): EIP-7623: remove blob in blob tx * fix(dependency): update prague execution specs * new(dependency): add joblib --------- Co-authored-by: spencer <[email protected]> Co-authored-by: Andrei Maiboroda <[email protected]> Co-authored-by: Paweł Bylica <[email protected]> Co-authored-by: Mario Vega <[email protected]> Co-authored-by: Dimitry Kh <[email protected]> Co-authored-by: raxhvl <[email protected]> Co-authored-by: raxhvl <[email protected]> Co-authored-by: Stuart Reed <[email protected]> Co-authored-by: felipe <[email protected]> Co-authored-by: danceratopz <[email protected]> Co-authored-by: pdobacz <[email protected]>
1 parent 8cb5063 commit 12850ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+6126
-771
lines changed

eels_resolutions.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
},
3737
"Prague": {
3838
"git_url": "https://github.com/sonhv0212/execution-specs.git",
39-
"branch": "devnets/prague/6",
40-
"commit": "51ba0176f320a79e161c27246f1d36b5919e6761"
39+
"branch": "prague/mario",
40+
"commit": "d97c29fd1b7ad48574e81d5f9a5593406c646f51"
4141
}
4242
}

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ dependencies = [
5151
"questionary @ git+https://github.com/tmbo/questionary@ff22aeae1cd9c1c734f14329934e349bec7873bc",
5252
"prompt_toolkit>=3.0.48,<4", # ensure we have a new enough version for ipython
5353
"ethereum-rlp>=0.1.3,<0.2",
54+
"joblib>=1.4.2",
5455
]
5556

5657
[project.urls]

src/ethereum_clis/clis/execution_specs.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ class ExecutionSpecsExceptionMapper(ExceptionMapper):
130130
@property
131131
def _mapping_data(self):
132132
return [
133+
ExceptionMessage(
134+
TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST,
135+
"Failed transaction: InvalidBlock()",
136+
),
137+
ExceptionMessage(
138+
TransactionException.SENDER_NOT_EOA,
139+
"Failed transaction: InvalidSenderError('not EOA')",
140+
),
133141
ExceptionMessage(
134142
TransactionException.TYPE_4_TX_CONTRACT_CREATION,
135143
"Failed transaction: ",

src/ethereum_test_base_types/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from .json import to_json
4040
from .pydantic import CamelModel, EthereumTestBaseModel, EthereumTestRootModel
4141
from .reference_spec import ReferenceSpec
42+
from .serialization import RLPSerializable, SignableRLPSerializable
4243

4344
__all__ = (
4445
"AccessList",
@@ -66,6 +67,8 @@
6667
"Number",
6768
"NumberBoundTypeVar",
6869
"ReferenceSpec",
70+
"RLPSerializable",
71+
"SignableRLPSerializable",
6972
"Storage",
7073
"StorageRootType",
7174
"TestAddress",

src/ethereum_test_base_types/composite_types.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .base_types import Address, Bytes, Hash, HashInt, HexNumber, ZeroPaddedHexNumber
99
from .conversions import BytesConvertible, NumberConvertible
1010
from .pydantic import CamelModel, EthereumTestRootModel
11+
from .serialization import RLPSerializable
1112

1213
StorageKeyValueTypeConvertible = NumberConvertible
1314
StorageKeyValueType = HashInt
@@ -460,15 +461,13 @@ class Alloc(EthereumTestRootModel[Dict[Address, Account | None]]):
460461
root: Dict[Address, Account | None] = Field(default_factory=dict, validate_default=True)
461462

462463

463-
class AccessList(CamelModel):
464+
class AccessList(CamelModel, RLPSerializable):
464465
"""Access List for transactions."""
465466

466467
address: Address
467468
storage_keys: List[Hash]
468469

469-
def to_list(self) -> List[Address | List[Hash]]:
470-
"""Return access list as a list of serializable elements."""
471-
return [self.address, self.storage_keys]
470+
rlp_fields: ClassVar[List[str]] = ["address", "storage_keys"]
472471

473472

474473
class ForkBlobSchedule(CamelModel):
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""Ethereum test types for serialization and encoding."""
2+
3+
from typing import Any, ClassVar, List
4+
5+
import ethereum_rlp as eth_rlp
6+
from ethereum_types.numeric import Uint
7+
8+
from ethereum_test_base_types import Bytes
9+
10+
11+
def to_serializable_element(v: Any) -> Any:
12+
"""Return a serializable element that can be passed to `eth_rlp.encode`."""
13+
if isinstance(v, int):
14+
return Uint(v)
15+
elif isinstance(v, bytes):
16+
return v
17+
elif isinstance(v, list):
18+
return [to_serializable_element(v) for v in v]
19+
elif isinstance(v, RLPSerializable):
20+
if v.signable:
21+
v.sign()
22+
return v.to_list(signing=False)
23+
elif v is None:
24+
return b""
25+
raise Exception(f"Unable to serialize element {v} of type {type(v)}.")
26+
27+
28+
class RLPSerializable:
29+
"""Class that adds RLP serialization to another class."""
30+
31+
rlp_override: Bytes | None = None
32+
33+
signable: ClassVar[bool] = False
34+
rlp_fields: ClassVar[List[str]]
35+
rlp_signing_fields: ClassVar[List[str]]
36+
37+
def get_rlp_fields(self) -> List[str]:
38+
"""
39+
Return an ordered list of field names to be included in RLP serialization.
40+
41+
Function can be overridden to customize the logic to return the fields.
42+
43+
By default, rlp_fields class variable is used.
44+
45+
The list can be nested list up to one extra level to represent nested fields.
46+
"""
47+
return self.rlp_fields
48+
49+
def get_rlp_signing_fields(self) -> List[str]:
50+
"""
51+
Return an ordered list of field names to be included in the RLP serialization of the object
52+
signature.
53+
54+
Function can be overridden to customize the logic to return the fields.
55+
56+
By default, rlp_signing_fields class variable is used.
57+
58+
The list can be nested list up to one extra level to represent nested fields.
59+
"""
60+
return self.rlp_signing_fields
61+
62+
def get_rlp_prefix(self) -> bytes:
63+
"""
64+
Return a prefix that has to be appended to the serialized object.
65+
66+
By default, an empty string is returned.
67+
"""
68+
return b""
69+
70+
def get_rlp_signing_prefix(self) -> bytes:
71+
"""
72+
Return a prefix that has to be appended to the serialized signing object.
73+
74+
By default, an empty string is returned.
75+
"""
76+
return b""
77+
78+
def sign(self):
79+
"""Sign the current object for further serialization."""
80+
raise NotImplementedError(f'Object "{self.__class__.__name__}" cannot be signed.')
81+
82+
def to_list_from_fields(self, fields: List[str]) -> List[Any]:
83+
"""
84+
Return an RLP serializable list that can be passed to `eth_rlp.encode`.
85+
86+
Can be for signing purposes or the entire object.
87+
"""
88+
values_list: List[Any] = []
89+
for field in fields:
90+
assert isinstance(field, str), (
91+
f'Unable to rlp serialize field "{field}" '
92+
f'in object type "{self.__class__.__name__}"'
93+
)
94+
assert hasattr(self, field), (
95+
f'Unable to rlp serialize field "{field}" '
96+
f'in object type "{self.__class__.__name__}"'
97+
)
98+
try:
99+
values_list.append(to_serializable_element(getattr(self, field)))
100+
except Exception as e:
101+
raise Exception(
102+
f'Unable to rlp serialize field "{field}" '
103+
f'in object type "{self.__class__.__name__}"'
104+
) from e
105+
return values_list
106+
107+
def to_list(self, signing: bool = False) -> List[Any]:
108+
"""
109+
Return an RLP serializable list that can be passed to `eth_rlp.encode`.
110+
111+
Can be for signing purposes or the entire object.
112+
"""
113+
field_list: List[str]
114+
if signing:
115+
if not self.signable:
116+
raise Exception(f'Object "{self.__class__.__name__}" does not support signing')
117+
field_list = self.get_rlp_signing_fields()
118+
else:
119+
if self.signable:
120+
# Automatically sign signable objects during full serialization:
121+
# Ensures nested objects have valid signatures in the final RLP.
122+
self.sign()
123+
field_list = self.get_rlp_fields()
124+
125+
return self.to_list_from_fields(field_list)
126+
127+
def rlp_signing_bytes(self) -> Bytes:
128+
"""Return the signing serialized envelope used for signing."""
129+
return Bytes(self.get_rlp_signing_prefix() + eth_rlp.encode(self.to_list(signing=True)))
130+
131+
def rlp(self) -> Bytes:
132+
"""Return the serialized object."""
133+
if self.rlp_override is not None:
134+
return self.rlp_override
135+
return Bytes(self.get_rlp_prefix() + eth_rlp.encode(self.to_list(signing=False)))
136+
137+
138+
class SignableRLPSerializable(RLPSerializable):
139+
"""Class that adds RLP serialization to another class with signing support."""
140+
141+
signable: ClassVar[bool] = True
142+
143+
def sign(self):
144+
"""Sign the current object for further serialization."""
145+
raise NotImplementedError(f'Object "{self.__class__.__name__}" needs to implement `sign`.')

src/ethereum_test_execution/transaction_post.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
class TransactionPost(BaseExecute):
1515
"""Represents a simple transaction-send then post-check execution format."""
1616

17-
transactions: List[Transaction]
17+
blocks: List[List[Transaction]]
1818
post: Alloc
1919

2020
execute_format_name: ClassVar[str] = "transaction_post"
@@ -24,17 +24,16 @@ class TransactionPost(BaseExecute):
2424

2525
def execute(self, eth_rpc: EthRPC):
2626
"""Execute the format."""
27-
if any(tx.error is not None for tx in self.transactions):
28-
for transaction in self.transactions:
29-
if transaction.error is None:
30-
eth_rpc.send_wait_transaction(transaction.with_signature_and_sender())
31-
else:
32-
with pytest.raises(SendTransactionExceptionError):
33-
eth_rpc.send_transaction(transaction.with_signature_and_sender())
34-
else:
35-
eth_rpc.send_wait_transactions(
36-
[tx.with_signature_and_sender() for tx in self.transactions]
37-
)
27+
for block in self.blocks:
28+
if any(tx.error is not None for tx in block):
29+
for transaction in block:
30+
if transaction.error is None:
31+
eth_rpc.send_wait_transaction(transaction.with_signature_and_sender())
32+
else:
33+
with pytest.raises(SendTransactionExceptionError):
34+
eth_rpc.send_transaction(transaction.with_signature_and_sender())
35+
else:
36+
eth_rpc.send_wait_transactions([tx.with_signature_and_sender() for tx in block])
3837

3938
for address, account in self.post.root.items():
4039
balance = eth_rpc.get_balance(address)

src/ethereum_test_fixtures/blockchain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def from_fixture_header(
206206
"""
207207
return cls(
208208
**header.model_dump(exclude={"rlp"}, exclude_none=True),
209-
transactions=[tx.rlp for tx in transactions],
209+
transactions=[tx.rlp() for tx in transactions],
210210
)
211211

212212

src/ethereum_test_fixtures/common.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
from typing import Dict
44

5-
from pydantic import Field
5+
from pydantic import AliasChoices, Field, model_serializer
66

77
from ethereum_test_base_types import (
88
BlobSchedule,
99
CamelModel,
1010
EthereumTestRootModel,
11+
SignableRLPSerializable,
1112
ZeroPaddedHexNumber,
1213
)
14+
from ethereum_test_types.types import Address, AuthorizationTupleGeneric
1315

1416

1517
class FixtureForkBlobSchedule(CamelModel):
@@ -35,3 +37,39 @@ def from_blob_schedule(
3537
return cls(
3638
root=blob_schedule.model_dump(),
3739
)
40+
41+
42+
class FixtureAuthorizationTuple(
43+
AuthorizationTupleGeneric[ZeroPaddedHexNumber], SignableRLPSerializable
44+
):
45+
"""Authorization tuple for fixture transactions."""
46+
47+
v: ZeroPaddedHexNumber = Field(validation_alias=AliasChoices("v", "yParity")) # type: ignore
48+
r: ZeroPaddedHexNumber
49+
s: ZeroPaddedHexNumber
50+
51+
signer: Address | None = None
52+
53+
@classmethod
54+
def from_authorization_tuple(
55+
cls, auth_tuple: AuthorizationTupleGeneric
56+
) -> "FixtureAuthorizationTuple":
57+
"""Return FixtureAuthorizationTuple from an AuthorizationTuple."""
58+
return cls(**auth_tuple.model_dump())
59+
60+
@model_serializer(mode="wrap", when_used="json-unless-none")
61+
def duplicate_v_as_y_parity(self, serializer):
62+
"""
63+
Add a duplicate 'yParity' field (same as `v`) in JSON fixtures.
64+
65+
Background: https://github.com/erigontech/erigon/issues/14073
66+
"""
67+
data = serializer(self)
68+
if "v" in data and data["v"] is not None:
69+
data["yParity"] = data["v"]
70+
return data
71+
72+
def sign(self):
73+
"""Sign the current object for further serialization."""
74+
# No-op, as the object is always already signed
75+
return

src/ethereum_test_rpc/rpc.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ def get_transaction_count(
136136
block = hex(block_number) if isinstance(block_number, int) else block_number
137137
return int(self.post_request("getTransactionCount", f"{address}", block), 16)
138138

139-
def get_transaction_by_hash(self, transaction_hash: Hash) -> TransactionByHashResponse:
139+
def get_transaction_by_hash(self, transaction_hash: Hash) -> TransactionByHashResponse | None:
140140
"""`eth_getTransactionByHash`: Returns transaction details."""
141141
try:
142-
resp = TransactionByHashResponse(
143-
**self.post_request("getTransactionByHash", f"{transaction_hash}")
144-
)
145-
return resp
142+
response = self.post_request("getTransactionByHash", f"{transaction_hash}")
143+
if response is None:
144+
return None
145+
return TransactionByHashResponse(**response)
146146
except ValidationError as e:
147147
pprint(e.errors())
148148
raise e
@@ -170,7 +170,9 @@ def send_raw_transaction(self, transaction_rlp: Bytes) -> Hash:
170170
def send_transaction(self, transaction: Transaction) -> Hash:
171171
"""`eth_sendRawTransaction`: Send a transaction to the client."""
172172
try:
173-
result_hash = Hash(self.post_request("sendRawTransaction", f"{transaction.rlp.hex()}"))
173+
result_hash = Hash(
174+
self.post_request("sendRawTransaction", f"{transaction.rlp().hex()}")
175+
)
174176
assert result_hash == transaction.hash
175177
assert result_hash is not None
176178
return transaction.hash
@@ -200,7 +202,7 @@ def wait_for_transaction(self, transaction: Transaction) -> TransactionByHashRes
200202
start_time = time.time()
201203
while True:
202204
tx = self.get_transaction_by_hash(tx_hash)
203-
if tx.block_number is not None:
205+
if tx is not None and tx.block_number is not None:
204206
return tx
205207
if (time.time() - start_time) > self.transaction_wait_timeout:
206208
break
@@ -225,7 +227,7 @@ def wait_for_transactions(
225227
while i < len(tx_hashes):
226228
tx_hash = tx_hashes[i]
227229
tx = self.get_transaction_by_hash(tx_hash)
228-
if tx.block_number is not None:
230+
if tx is not None and tx.block_number is not None:
229231
responses.append(tx)
230232
tx_hashes.pop(i)
231233
else:

0 commit comments

Comments
 (0)