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
3 changes: 2 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ Users can select any of the artifacts depending on their testing needs for their

#### `execute`

- ✨ Add `blob_transaction_test` execute test spec, which allows tests that send blob transactions to a running client and verifying its `engine_getBlobsVX` endpoint behavior ([#1644](https://github.com/ethereum/execution-spec-tests/pull/1644)).
- ✨ Added new `Blob` class which can use the ckzg library to generate valid blobs at runtime ([#1614](https://github.com/ethereum/execution-spec-tests/pull/1614)).
- ✨ Added `blob_transaction_test` execute test spec, which allows tests that send blob transactions to a running client and verifying its `engine_getBlobsVX` endpoint behavior ([#1644](https://github.com/ethereum/execution-spec-tests/pull/1644)).

### πŸ“‹ Misc

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ dependencies = [
"pytest-regex>=0.2.0,<0.3",
"eth-abi>=5.2.0",
"joblib>=1.4.2",
"ckzg>=2.1.1",
]

[project.urls]
Expand Down
47 changes: 37 additions & 10 deletions src/ethereum_test_execution/blob_transaction.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Test execution format to get blobs from the execution client."""

from hashlib import sha256
from typing import ClassVar, Dict, List

from ethereum_test_base_types import Hash
from ethereum_test_base_types.base_types import Bytes
from ethereum_test_forks import Fork
from ethereum_test_rpc import BlobAndProofV1, BlobAndProofV2, EngineRPC, EthRPC
from ethereum_test_types import NetworkWrappedTransaction, Transaction
Expand All @@ -18,18 +20,19 @@ def versioned_hashes_with_blobs_and_proofs(
proofs.
"""
versioned_hashes: Dict[Hash, BlobAndProofV1 | BlobAndProofV2] = {}
for blob in tx.blobs:
versioned_hash = blob.versioned_hash()
if blob.kzg_proof is not None:
versioned_hashes[versioned_hash] = BlobAndProofV1(blob=blob.data, proof=blob.kzg_proof)
elif blob.kzg_cell_proofs is not None:
versioned_hashes[versioned_hash] = BlobAndProofV2(
blob=blob.data, proofs=blob.kzg_cell_proofs
for blob in tx.blob_objects:
if isinstance(blob.proof, Bytes):
versioned_hashes[blob.versioned_hash] = BlobAndProofV1(
blob=blob.data, proof=blob.proof
)
elif isinstance(blob.proof, list):
versioned_hashes[blob.versioned_hash] = BlobAndProofV2(
blob=blob.data, proofs=blob.proof
)
else:
raise ValueError(
f"Blob with versioned hash {versioned_hash.hex()} requires either kzg_proof "
"or kzg_cell_proofs, but both are None"
f"Blob with versioned hash {blob.versioned_hash.hex()} requires a proof "
"that is not None"
)

return versioned_hashes
Expand Down Expand Up @@ -96,7 +99,31 @@ def execute(self, fork: Fork, eth_rpc: EthRPC, engine_rpc: EngineRPC | None):
if expected_blob.blob != received_blob.blob:
raise ValueError("Blob mismatch.")
if expected_blob.proofs != received_blob.proofs:
raise ValueError("Proofs mismatch.")
error_message = "Proofs mismatch."
error_message += f"len(expected_blob.proofs) = {len(expected_blob.proofs)}, "
error_message += f"len(received_blob.proofs) = {len(received_blob.proofs)}\n"
if len(expected_blob.proofs) == len(received_blob.proofs):
index = 0

for expected_proof, received_proof in zip(
expected_blob.proofs, received_blob.proofs, strict=False
):
if len(expected_proof) != len(received_proof):
error_message += f"Proof length mismatch. index = {index},"
error_message += f"expected_proof length = {len(expected_proof)}, "
error_message += f"received_proof length = {len(received_proof)}\n"
index += 1
continue
if expected_proof != received_proof:
error_message += f"Proof mismatch. index = {index},"
error_message += (
f"expected_proof hash = {sha256(expected_proof).hexdigest()}, "
)
error_message += (
f"received_proof hash = {sha256(received_proof).hexdigest()}\n"
)
index += 1
raise ValueError(error_message)
else:
raise ValueError(f"Unexpected blob type: {type(expected_blob)}")

Expand Down
23 changes: 22 additions & 1 deletion src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
"""Abstract base class for Ethereum forks."""

from abc import ABC, ABCMeta, abstractmethod
from typing import Any, ClassVar, List, Mapping, Optional, Protocol, Sized, Tuple, Type
from typing import (
Any,
ClassVar,
Dict,
List,
Literal,
Mapping,
Optional,
Protocol,
Sized,
Tuple,
Type,
Union,
)

from semver import Version

Expand Down Expand Up @@ -155,6 +168,14 @@ class BaseFork(ABC, metaclass=BaseForkMeta):
_solc_name: ClassVar[Optional[str]] = None
_ignore: ClassVar[bool] = False

# make mypy happy
BLOB_CONSTANTS: ClassVar[Dict[str, Union[int, Literal["big"]]]] = {}

@classmethod
def get_blob_constant(cls, name: str) -> int | Literal["big"]:
"""Return value of requested blob constant."""
raise NotImplementedError

def __init_subclass__(
cls,
*,
Expand Down
39 changes: 38 additions & 1 deletion src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from hashlib import sha256
from os.path import realpath
from pathlib import Path
from typing import List, Mapping, Optional, Sized, Tuple
from typing import List, Literal, Mapping, Optional, Sized, Tuple

from semver import Version

Expand Down Expand Up @@ -908,6 +908,27 @@ def valid_opcodes(
class Cancun(Shanghai):
"""Cancun fork."""

BLOB_CONSTANTS = { # every value is an int or a Literal
"FIELD_ELEMENTS_PER_BLOB": 4096,
"BYTES_PER_FIELD_ELEMENT": 32,
"CELL_LENGTH": 2048,
"BLS_MODULUS": 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001, # EIP-2537: Main subgroup order = q, due to this BLS_MODULUS every blob byte (uint256) must be smaller than 116 # noqa: E501
# https://github.com/ethereum/consensus-specs/blob/cc6996c22692d70e41b7a453d925172ee4b719ad/specs/deneb/polynomial-commitments.md?plain=1#L78
"BYTES_PER_PROOF": 48,
"BYTES_PER_COMMITMENT": 48,
"KZG_ENDIANNESS": "big",
"AMOUNT_CELL_PROOFS": 0,
}

@classmethod
def get_blob_constant(cls, name: str) -> int | Literal["big"]:
"""Return blob constant if it exists."""
retrieved_constant = cls.BLOB_CONSTANTS.get(name)
assert retrieved_constant is not None, (
f"You tried to retrieve the blob constant {name} but it does not exist!"
)
return retrieved_constant

@classmethod
def solc_min_version(cls) -> Version:
"""Return minimum version of solc that supports this fork."""
Expand Down Expand Up @@ -1092,6 +1113,16 @@ def valid_opcodes(
class Prague(Cancun):
"""Prague fork."""

# update some blob constants
BLOB_CONSTANTS = {
**Cancun.BLOB_CONSTANTS, # same base constants as cancun
"MAX_BLOBS_PER_BLOCK": 9, # but overwrite or add these
"TARGET_BLOBS_PER_BLOCK": 6,
"MAX_BLOB_GAS_PER_BLOCK": 1179648,
"TARGET_BLOB_GAS_PER_BLOCK": 786432,
"BLOB_BASE_FEE_UPDATE_FRACTION": 5007716,
}

@classmethod
def is_deployed(cls) -> bool:
"""
Expand Down Expand Up @@ -1343,6 +1374,12 @@ def engine_forkchoice_updated_version(
class Osaka(Prague, solc_name="cancun"):
"""Osaka fork."""

# update some blob constants
BLOB_CONSTANTS = {
**Prague.BLOB_CONSTANTS, # same base constants as prague
"AMOUNT_CELL_PROOFS": 128,
}

@classmethod
def engine_get_payload_version(
cls, block_number: int = 0, timestamp: int = 0
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum_test_rpc/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ def blob_versioned_hashes(self, versioned_hash_version: int = 1) -> List[Hash]:


class BlobAndProofV1(CamelModel):
"""Represents a blob and single-proof structure."""
"""Represents a blob and single-proof structure (< Osaka)."""

blob: Bytes
proof: Bytes


class BlobAndProofV2(CamelModel):
"""Represents a blob and proof structure."""
"""Represents a blob and cell proof structure (>= Osaka)."""

blob: Bytes
proofs: List[Bytes]
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum_test_types/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Common definitions and types."""

from .account_types import EOA, Alloc
from .blob_types import Blob
from .block_types import (
Environment,
EnvironmentDefaults,
Expand All @@ -23,7 +24,6 @@
)
from .transaction_types import (
AuthorizationTuple,
Blob,
NetworkWrappedTransaction,
Transaction,
TransactionDefaults,
Expand Down
Loading
Loading