Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/testing/src/execution_testing/base_types/pydantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ def copy(self: Self, **kwargs: Any) -> Self:
"""
Create a copy of the model with the updated fields that are validated.
"""
return self.__class__(**(self.model_dump(exclude_unset=True) | kwargs))
# Only include actual model fields, not computed fields
model_field_names = set(self.__class__.model_fields.keys())
dumped = {
k: v
for k, v in self.model_dump(exclude_unset=True).items()
if k in model_field_names
}
return self.__class__(**(dumped | kwargs))


class CamelModel(CopyValidateModel):
Expand All @@ -47,4 +54,5 @@ class CamelModel(CopyValidateModel):
alias_generator=to_camel,
populate_by_name=True,
validate_default=True,
extra="forbid",
)
4 changes: 2 additions & 2 deletions packages/testing/src/execution_testing/cli/eofwrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ def _wrap_fixture(
fixture_tx_dump.pop("ty")
fixture_tx_dump.pop("data")
tx = Transaction(
type=fixture_tx.ty,
input=fixture_tx.data,
ty=fixture_tx.ty,
data=fixture_tx.data,
**fixture_tx_dump,
)
block.txs.append(tx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ def preprocess_fork_times_blocks(cls, data: Any) -> Any:
if synonym:
stripped_key = synonym
else:
# Remove deprecated fork keys that have no synonym
data.pop(key)
continue
fork_activation_times[stripped_key] = data.pop(key)
if fork_activation_times:
Expand Down Expand Up @@ -360,7 +362,8 @@ class Genesis(CamelModel):
def hash(self) -> Hash:
"""Calculate the genesis hash."""
dumped_genesis = self.model_dump(
mode="json", exclude={"config", "alloc"}
mode="json",
exclude={"config", "alloc", "nonce", "mixhash", "parent_hash"},
)
genesis_fork = self.config.fork()
env = Environment(**dumped_genesis).set_fork_requirements(genesis_fork)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,44 @@ def genesis_contents(genesis_file_name: str) -> str:
),
blob_schedule={
Cancun: ForkConfigBlobSchedule(
target=3, max=6, baseFeeUpdateFraction=3338477
target_blobs_per_block=3,
max_blobs_per_block=6,
base_fee_update_fraction=3338477,
),
Prague: ForkConfigBlobSchedule(
target=6, max=9, baseFeeUpdateFraction=5007716
target_blobs_per_block=6,
max_blobs_per_block=9,
base_fee_update_fraction=5007716,
),
Osaka: ForkConfigBlobSchedule(
target=6, max=9, baseFeeUpdateFraction=5007716
target_blobs_per_block=6,
max_blobs_per_block=9,
base_fee_update_fraction=5007716,
),
BPO1: ForkConfigBlobSchedule(
target=9, max=12, baseFeeUpdateFraction=5007716
target_blobs_per_block=9,
max_blobs_per_block=12,
base_fee_update_fraction=5007716,
),
BPO2: ForkConfigBlobSchedule(
target=12, max=15, baseFeeUpdateFraction=5007716
target_blobs_per_block=12,
max_blobs_per_block=15,
base_fee_update_fraction=5007716,
),
BPO3: ForkConfigBlobSchedule(
target=15, max=18, baseFeeUpdateFraction=5007716
target_blobs_per_block=15,
max_blobs_per_block=18,
base_fee_update_fraction=5007716,
),
BPO4: ForkConfigBlobSchedule(
target=6, max=9, baseFeeUpdateFraction=5007716
target_blobs_per_block=6,
max_blobs_per_block=9,
base_fee_update_fraction=5007716,
),
BPO5: ForkConfigBlobSchedule(
target=15, max=20, baseFeeUpdateFraction=5007716
target_blobs_per_block=15,
max_blobs_per_block=20,
base_fee_update_fraction=5007716,
),
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def test_init_use_pre_alloc(self) -> None:
pre=Alloc().model_dump(mode="json"),
environment=Environment()
.set_fork_requirements(Prague)
.model_dump(mode="json"),
network=Prague.name(),
.model_dump(mode="json", exclude={"parent_hash"}),
fork=Prague.name(),
)
test_group = test_group_builder.build()
mock_groups = PreAllocGroups(root={"test_hash": test_group})
Expand Down Expand Up @@ -171,8 +171,8 @@ def test_get_pre_alloc_group(self) -> None:
pre=Alloc().model_dump(mode="json"),
environment=Environment()
.set_fork_requirements(Prague)
.model_dump(mode="json"),
network=Prague.name(),
.model_dump(mode="json", exclude={"parent_hash"}),
fork=Prague.name(),
)
test_group = test_group_builder.build()
mock_groups = PreAllocGroups(root={"test_hash": test_group})
Expand Down Expand Up @@ -239,8 +239,8 @@ def test_update_pre_alloc_group(self) -> None:
pre=Alloc().model_dump(mode="json"),
environment=Environment()
.set_fork_requirements(Prague)
.model_dump(mode="json"),
network=Prague.name(),
.model_dump(mode="json", exclude={"parent_hash"}),
fork=Prague.name(),
)
session.update_pre_alloc_group_builder("test_hash", test_group_builder)

Expand All @@ -265,8 +265,8 @@ def test_update_pre_alloc_group_wrong_phase(self) -> None:
pre=Alloc().model_dump(mode="json"),
environment=Environment()
.set_fork_requirements(Prague)
.model_dump(mode="json"),
network=Prague.name(),
.model_dump(mode="json", exclude={"parent_hash"}),
fork=Prague.name(),
)
with pytest.raises(
ValueError,
Expand All @@ -291,8 +291,8 @@ def test_save_pre_alloc_groups(self) -> None:
pre=Alloc().model_dump(mode="json"),
environment=Environment()
.set_fork_requirements(Prague)
.model_dump(mode="json"),
network=Prague.name(),
.model_dump(mode="json", exclude={"parent_hash"}),
fork=Prague.name(),
)
session.update_pre_alloc_group_builder("test_hash", test_group_builder)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class ClientFile(RootModel, YAMLModel):
class HiveInfo(CamelModel):
"""Hive instance information."""

# Allow extra fields: HiveInfo parses external hive API responses which
# may include fields not defined in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

command: List[str]
client_file: ClientFile = Field(
default_factory=lambda: ClientFile(root=[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class TraceLine(CamelModel):
refund: int
op_name: str
error: str | None = None
return_data: str | None = None

def are_equivalent(self, other: Self) -> bool:
"""Return True if the only difference is the gas counter."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,6 @@ def test_evm_t8n(
for key in missing_receipt_fields:
for i, _ in enumerate(expected.get("result")["receipts"]):
del expected.get("result")["receipts"][i][key]
for i, receipt in enumerate(expected.get("result")["receipts"]):
if int(receipt["logsBloom"], 16) == 0:
del expected.get("result")["receipts"][i]["logsBloom"]

t8n_result = to_json(t8n_output.result)
for i, _ in enumerate(expected.get("result")["rejected"]):
Expand Down
31 changes: 30 additions & 1 deletion packages/testing/src/execution_testing/fixtures/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ class FixtureHeader(CamelModel):
We combine the `Environment` and `Result` contents to create this model.
"""

# Allow extra fields: FixtureHeader is constructed from merged Result and
# Environment data via model_dump(), which includes fields not in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

parent_hash: Hash = Hash(0)
ommers_hash: Hash = Field(Hash(EmptyOmmersRoot), alias="uncleHash")
fee_recipient: Address = Field(
Expand Down Expand Up @@ -293,6 +297,10 @@ class FixtureExecutionPayload(CamelModel):
Representation of an Ethereum execution payload within a test Fixture.
"""

# Allow extra fields: FixtureExecutionPayload is constructed from
# FixtureHeader via model_dump(), which includes fields not in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

parent_hash: Hash
fee_recipient: Address
state_root: Hash
Expand Down Expand Up @@ -333,7 +341,7 @@ def from_fixture_header(
transactions, a list of withdrawals, and an optional block access list.
"""
return cls(
**header.model_dump(exclude={"rlp"}, exclude_none=True),
**header.model_dump(exclude_none=True),
transactions=[tx.rlp() for tx in transactions],
withdrawals=withdrawals,
block_access_list=block_access_list,
Expand Down Expand Up @@ -460,6 +468,10 @@ class FixtureTransaction(
):
"""Representation of an Ethereum transaction within a test Fixture."""

# Allow extra fields: FixtureTransaction is constructed from
# Transaction via model_dump(), which includes fields not in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

authorization_list: List[FixtureAuthorizationTuple] | None = None
initcodes: List[Bytes] | None = None

Expand Down Expand Up @@ -505,6 +517,18 @@ class FixtureBlockBase(CamelModel):
bytes.
"""

@model_validator(mode="before")
@classmethod
def strip_block_number_computed_field(cls, data: Any) -> Any:
"""
Strip the block_number computed field which gets included in model_dump()
but is not a valid input field.
"""
if isinstance(data, dict):
data.pop("blocknumber", None)
data.pop("block_number", None)
return data

header: FixtureHeader = Field(..., alias="blockHeader")
txs: List[FixtureTransaction] = Field(
default_factory=list, alias="transactions"
Expand All @@ -517,6 +541,7 @@ class FixtureBlockBase(CamelModel):
block_access_list: BlockAccessList | None = Field(
None, description="EIP-7928 Block Access List"
)
fork: Fork | None = Field(None, exclude=True)

@computed_field(alias="blocknumber") # type: ignore[prop-decorator]
@cached_property
Expand Down Expand Up @@ -677,6 +702,10 @@ class BlockchainEngineXFixture(BlockchainEngineFixtureCommon):
test execution without client restarts.
"""

# Allow extra fields: BlockchainEngineXFixture is constructed from shared
# fixture_data that includes fields for other fixture formats (e.g. genesis).
model_config = CamelModel.model_config | {"extra": "ignore"}

format_name: ClassVar[str] = "blockchain_test_engine_x"
description: ClassVar[str] = (
"Tests that generate a Blockchain Test Engine X fixture."
Expand Down
24 changes: 21 additions & 3 deletions packages/testing/src/execution_testing/fixtures/common.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Common types used to define multiple fixture types."""

from typing import Dict
from typing import Any, Dict

from pydantic import AliasChoices, Field
from pydantic import AliasChoices, Field, model_validator

from execution_testing.base_types import (
BlobSchedule,
Expand Down Expand Up @@ -51,6 +51,10 @@ class FixtureAuthorizationTuple(
):
"""Authorization tuple for fixture transactions."""

# Allow extra fields: FixtureAuthorizationTuple is constructed from
# AuthorizationTuple via model_dump(), which includes fields not in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

v: ZeroPaddedHexNumber = Field(
validation_alias=AliasChoices("v", "yParity")
)
Expand All @@ -59,12 +63,26 @@ class FixtureAuthorizationTuple(

signer: Address | None = None

@model_validator(mode="before")
@classmethod
def strip_y_parity_duplicate(cls, data: Any) -> Any:
"""
Strip yParity if v is present since yParity is added as a duplicate
during serialization for compatibility.
"""
if isinstance(data, dict) and "v" in data and "yParity" in data:
data.pop("yParity")
return data

@classmethod
def from_authorization_tuple(
cls, auth_tuple: AuthorizationTupleGeneric
) -> "FixtureAuthorizationTuple":
"""Return FixtureAuthorizationTuple from an AuthorizationTuple."""
return cls(**auth_tuple.model_dump())
# Exclude fields that don't exist in FixtureAuthorizationTuple
auth_dump = auth_tuple.model_dump()
auth_dump.pop("secret_key", None)
return cls(**auth_dump)

def sign(self) -> None:
"""Sign the current object for further serialization."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,6 @@ class PreAllocGroup(PreAllocGroupBuilder):
pre-allocation group optimization.
"""

# Allow both field names and aliases
model_config = {"populate_by_name": True}
pre: GroupPreAlloc
genesis: FixtureHeader
pre_account_count: int
Expand Down
8 changes: 8 additions & 0 deletions packages/testing/src/execution_testing/fixtures/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@
class FixtureEnvironment(EnvironmentGeneric[ZeroPaddedHexNumber]):
"""Type used to describe the environment of a state test."""

# Allow extra fields: FixtureEnvironment is constructed from Environment
# via model_dump(), which includes many fields not in EnvironmentGeneric.
model_config = CamelModel.model_config | {"extra": "ignore"}

prev_randao: Hash | None = Field(None, alias="currentRandom") # type: ignore


class FixtureTransaction(TransactionFixtureConverter):
"""Type used to describe a transaction in a state test."""

# Allow extra fields: FixtureTransaction is constructed from Transaction
# via model_dump(), which includes many fields not in this model.
model_config = CamelModel.model_config | {"extra": "ignore"}

nonce: ZeroPaddedHexNumber
gas_price: ZeroPaddedHexNumber | None = None
max_priority_fee_per_gas: ZeroPaddedHexNumber | None = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def test_json_dict() -> None:
"""Test that the json_dict property does not include the info field."""
fixture = TransactionFixture(
txbytes="0x1234",
transaction="0x1234",
result={"Paris": FixtureResult(intrinsic_gas=0)},
)
assert "_info" not in fixture.json_dict, (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@
blob_gas_used=17,
excess_blob_gas=18,
),
transactions=[
txs=[
FixtureTransaction.from_transaction(
Transaction().with_signature_and_sender()
)
Expand Down Expand Up @@ -470,7 +470,7 @@
blob_gas_used=17,
excess_blob_gas=18,
),
transactions=[
txs=[
FixtureTransaction.from_transaction(
Transaction(to=None).with_signature_and_sender()
)
Expand Down
2 changes: 2 additions & 0 deletions packages/testing/src/execution_testing/rpc/rpc_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ class PayloadAttributes(CamelModel):
suggested_fee_recipient: Address
withdrawals: List[Withdrawal] | None = None
parent_beacon_block_root: Hash | None = None
target_blobs_per_block: HexNumber | None = None
max_blobs_per_block: HexNumber | None = None


class BlobsBundle(CamelModel):
Expand Down
5 changes: 4 additions & 1 deletion packages/testing/src/execution_testing/specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,10 @@ class Header(CamelModel):
```
"""

model_config = ConfigDict(arbitrary_types_allowed=True)
model_config = ConfigDict(
**CamelModel.model_config,
arbitrary_types_allowed=True,
)

@model_serializer(mode="wrap", when_used="json")
def _serialize_model(self, serializer: Any, info: Any) -> Dict[str, Any]:
Expand Down
Loading
Loading