Skip to content

Commit df94d6f

Browse files
committed
feat(execution,specs): Implement BlobsTest execute-only spec type
1 parent d44f7c6 commit df94d6f

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""Ethereum test execution package."""
22

33
from .base import BaseExecute, ExecuteFormat, LabeledExecuteFormat
4+
from .get_blobs import GetBlobs
45
from .transaction_post import TransactionPost
56

67
__all__ = [
78
"BaseExecute",
89
"ExecuteFormat",
10+
"GetBlobs",
911
"LabeledExecuteFormat",
1012
"TransactionPost",
1113
]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""Test execution format to get blobs from the execution client."""
2+
3+
from typing import ClassVar, Dict, List
4+
5+
from ethereum_test_base_types import Hash
6+
from ethereum_test_forks import Fork
7+
from ethereum_test_rpc import BlobAndProof, EngineRPC, EthRPC
8+
from ethereum_test_types import NetworkWrappedTransaction, Transaction
9+
10+
from .base import BaseExecute
11+
12+
13+
def versioned_hashes_with_blobs_and_proofs(
14+
tx: NetworkWrappedTransaction,
15+
) -> Dict[Hash, BlobAndProof]:
16+
"""
17+
Return a dictionary of versioned hashes with their corresponding blobs and
18+
proofs.
19+
"""
20+
versioned_hashes: Dict[Hash, BlobAndProof] = {}
21+
for blob in tx.blobs:
22+
versioned_hash = blob.versioned_hash()
23+
if blob.kzg_proof is not None:
24+
versioned_hashes[versioned_hash] = BlobAndProof(blob=blob.data, proof=blob.kzg_proof)
25+
elif blob.kzg_cell_proofs is not None:
26+
versioned_hashes[versioned_hash] = BlobAndProof(
27+
blob=blob.data, proofs=blob.kzg_cell_proofs
28+
)
29+
else:
30+
raise ValueError("Invalid blob: missing kzg proof or cell proofs")
31+
return versioned_hashes
32+
33+
34+
class GetBlobs(BaseExecute):
35+
"""
36+
Represents a test execution format to send blob transactions to the client and then
37+
use `engine_getBlobsV*` end points to validate the proofs generated by the execution client.
38+
"""
39+
40+
format_name: ClassVar[str] = "get_blobs"
41+
description: ClassVar[str] = "Get blobs from the execution client"
42+
requires_engine_rpc: ClassVar[bool] = True
43+
44+
txs: List[NetworkWrappedTransaction | Transaction]
45+
46+
def execute(self, fork: Fork, eth_rpc: EthRPC, engine_rpc: EngineRPC | None):
47+
"""Execute the format."""
48+
assert engine_rpc is not None, "Engine RPC is required for this format."
49+
versioned_hashes: Dict[Hash, BlobAndProof] = {}
50+
sent_txs: List[Transaction] = []
51+
for tx in self.txs:
52+
if isinstance(tx, NetworkWrappedTransaction):
53+
tx.tx = tx.tx.with_signature_and_sender()
54+
sent_txs.append(tx.tx)
55+
expected_hash = tx.tx.hash
56+
versioned_hashes.update(versioned_hashes_with_blobs_and_proofs(tx))
57+
else:
58+
tx = tx.with_signature_and_sender()
59+
sent_txs.append(tx)
60+
expected_hash = tx.hash
61+
received_hash = eth_rpc.send_raw_transaction(tx.rlp())
62+
assert expected_hash == received_hash, (
63+
f"Expected hash {expected_hash} does not match received hash {received_hash}."
64+
)
65+
version = fork.engine_get_blobs_version()
66+
assert version is not None, "Engine get blobs version is not supported by the fork."
67+
resp = engine_rpc.get_blobs(list(versioned_hashes.keys()), version=version)
68+
local_blobs_and_proofs = list(versioned_hashes.values())
69+
assert len(resp) == len(local_blobs_and_proofs), (
70+
f"Expected {len(local_blobs_and_proofs)} blobs and proofs, got {len(resp)}."
71+
)
72+
for local, remote in zip(local_blobs_and_proofs, resp.root, strict=False):
73+
if remote is None:
74+
raise ValueError("Remote blob is None.")
75+
assert local.blob == remote.blob, "Blob mismatch."
76+
assert local.proofs == remote.proofs, "Proofs mismatch."
77+
78+
eth_rpc.wait_for_transactions(sent_txs)

src/ethereum_test_specs/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from .base import BaseTest, TestSpec
44
from .base_static import BaseStaticTest
5+
from .blobs import BlobsTest, BlobsTestFiller, BlobsTestSpec
56
from .blockchain import (
67
BlockchainTest,
78
BlockchainTestFiller,
@@ -22,6 +23,9 @@
2223
__all__ = (
2324
"BaseStaticTest",
2425
"BaseTest",
26+
"BlobsTest",
27+
"BlobsTestFiller",
28+
"BlobsTestSpec",
2529
"BlockchainTest",
2630
"BlockchainTestEngineFiller",
2731
"BlockchainTestEngineSpec",

src/ethereum_test_specs/blobs.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Test specification for blob tests."""
2+
3+
from typing import Callable, ClassVar, Generator, List, Optional, Sequence, Type
4+
5+
from ethereum_clis import TransitionTool
6+
from ethereum_test_base_types import Alloc
7+
from ethereum_test_execution import BaseExecute, GetBlobs
8+
from ethereum_test_fixtures import (
9+
BaseFixture,
10+
FixtureFormat,
11+
)
12+
from ethereum_test_forks import Fork
13+
from ethereum_test_types import NetworkWrappedTransaction, Transaction
14+
15+
from .base import BaseTest, ExecuteFormat, LabeledExecuteFormat
16+
17+
18+
class BlobsTest(BaseTest):
19+
"""Test specification for blob tests."""
20+
21+
pre: Alloc
22+
txs: List[NetworkWrappedTransaction | Transaction]
23+
24+
supported_execute_formats: ClassVar[Sequence[LabeledExecuteFormat]] = [
25+
LabeledExecuteFormat(
26+
GetBlobs,
27+
"get_blobs",
28+
"A test that executes a get blobs request",
29+
),
30+
]
31+
32+
def generate(
33+
self,
34+
*,
35+
t8n: TransitionTool,
36+
fork: Fork,
37+
fixture_format: FixtureFormat,
38+
eips: Optional[List[int]] = None,
39+
) -> BaseFixture:
40+
"""Generate the list of test fixtures."""
41+
raise Exception(f"Unknown fixture format: {fixture_format}")
42+
43+
def execute(
44+
self,
45+
*,
46+
fork: Fork,
47+
execute_format: ExecuteFormat,
48+
eips: Optional[List[int]] = None,
49+
) -> BaseExecute:
50+
"""Generate the list of test fixtures."""
51+
if execute_format == GetBlobs:
52+
return GetBlobs(
53+
txs=self.txs,
54+
)
55+
raise Exception(f"Unsupported execute format: {execute_format}")
56+
57+
58+
BlobsTestSpec = Callable[[str], Generator[BlobsTest, None, None]]
59+
BlobsTestFiller = Type[BlobsTest]

src/ethereum_test_tools/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from ethereum_test_fixtures import BaseFixture, FixtureCollector
2525
from ethereum_test_specs import (
2626
BaseTest,
27+
BlobsTest,
28+
BlobsTestFiller,
2729
BlockchainTest,
2830
BlockchainTestFiller,
2931
EOFStateTest,
@@ -99,6 +101,8 @@
99101
"BaseFixture",
100102
"BaseTest",
101103
"Blob",
104+
"BlobsTest",
105+
"BlobsTestFiller",
102106
"Block",
103107
"BlockchainTest",
104108
"BlockchainTestFiller",

0 commit comments

Comments
 (0)