Skip to content

Commit 50bc408

Browse files
committed
feat(execution,specs): Implement BlobsTest execute-only spec type
Update exception message in src/ethereum_test_execution/get_blobs.py Co-authored-by: danceratopz <[email protected]> Update description src/ethereum_test_execution/get_blobs.py Co-authored-by: danceratopz <[email protected]> Apply suggestions from code review to src/ethereum_test_execution/get_blobs.py Co-authored-by: danceratopz <[email protected]> Update docs/CHANGELOG.md for ethereum_test_execution Co-authored-by: danceratopz <[email protected]> fix ethereum_test_execution comment fix ethereum_test_execution tox fix(execution): Use BlobAndProofV1 and BlobAndProofV2 fix(execution): Rename blob_transaction_test fix(sepcs): Rename blob_transaction_test
1 parent 1eb6b58 commit 50bc408

File tree

7 files changed

+177
-1
lines changed

7 files changed

+177
-1
lines changed

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Users can select any of the artifacts depending on their testing needs for their
3434

3535
#### `consume`
3636

37+
#### `execute`
38+
39+
- ✨ Add blob-transaction execute test spec, which allows to write tests that send blob transactions to a running client and verify `engine_getBlobsVX` endpoint behavior ([#1644](https://github.com/ethereum/execution-spec-tests/pull/1644)).
40+
3741
### 📋 Misc
3842

3943
- ✨ Added the [EIP checklist template](https://eest.ethereum.org/main/writing_tests/checklist_templates/eip_testing_checklist_template/) that serves as a reference to achieve better coverage when implementing tests for new EIPs ([#1327](https://github.com/ethereum/execution-spec-tests/pull/1327)).
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 .blob_transaction import BlobTransaction
45
from .transaction_post import TransactionPost
56

67
__all__ = [
78
"BaseExecute",
89
"ExecuteFormat",
10+
"BlobTransaction",
911
"LabeledExecuteFormat",
1012
"TransactionPost",
1113
]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 BlobAndProofV1, BlobAndProofV2, 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, BlobAndProofV1 | BlobAndProofV2]:
16+
"""
17+
Return a dictionary of versioned hashes with their corresponding blobs and
18+
proofs.
19+
"""
20+
versioned_hashes: Dict[Hash, BlobAndProofV1 | BlobAndProofV2] = {}
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] = BlobAndProofV1(blob=blob.data, proof=blob.kzg_proof)
25+
elif blob.kzg_cell_proofs is not None:
26+
versioned_hashes[versioned_hash] = BlobAndProofV2(
27+
blob=blob.data, proofs=blob.kzg_cell_proofs
28+
)
29+
else:
30+
raise ValueError(
31+
f"Blob with versioned hash {versioned_hash.hex()} requires either kzg_proof "
32+
"or kzg_cell_proofs, but both are None"
33+
)
34+
35+
return versioned_hashes
36+
37+
38+
class BlobTransaction(BaseExecute):
39+
"""
40+
Represents a test execution format to send blob transactions to the client and then
41+
use `engine_getBlobsV*` end points to validate the proofs generated by the execution client.
42+
"""
43+
44+
format_name: ClassVar[str] = "blob_transaction_test"
45+
description: ClassVar[str] = (
46+
"Send blob transactions to the execution client and validate their availability via "
47+
"`engine_getBlobsV*`"
48+
)
49+
requires_engine_rpc: ClassVar[bool] = True
50+
51+
txs: List[NetworkWrappedTransaction | Transaction]
52+
53+
def execute(self, fork: Fork, eth_rpc: EthRPC, engine_rpc: EngineRPC | None):
54+
"""Execute the format."""
55+
assert engine_rpc is not None, "Engine RPC is required for this format."
56+
versioned_hashes: Dict[Hash, BlobAndProofV1 | BlobAndProofV2] = {}
57+
sent_txs: List[Transaction] = []
58+
for tx in self.txs:
59+
if isinstance(tx, NetworkWrappedTransaction):
60+
tx.tx = tx.tx.with_signature_and_sender()
61+
sent_txs.append(tx.tx)
62+
expected_hash = tx.tx.hash
63+
versioned_hashes.update(versioned_hashes_with_blobs_and_proofs(tx))
64+
else:
65+
tx = tx.with_signature_and_sender()
66+
sent_txs.append(tx)
67+
expected_hash = tx.hash
68+
received_hash = eth_rpc.send_raw_transaction(tx.rlp())
69+
assert expected_hash == received_hash, (
70+
f"Expected hash {expected_hash} does not match received hash {received_hash}."
71+
)
72+
version = fork.engine_get_blobs_version()
73+
assert version is not None, "Engine get blobs version is not supported by the fork."
74+
blob_response = engine_rpc.get_blobs(list(versioned_hashes.keys()), version=version)
75+
local_blobs_and_proofs = list(versioned_hashes.values())
76+
if len(blob_response) != len(local_blobs_and_proofs):
77+
raise ValueError(
78+
f"Expected {len(local_blobs_and_proofs)} blobs and proofs, "
79+
f"got {len(blob_response)}."
80+
)
81+
for expected_blob, received_blob in zip(
82+
local_blobs_and_proofs, blob_response.root, strict=False
83+
):
84+
if received_blob is None:
85+
raise ValueError("Received blob is empty.")
86+
if isinstance(expected_blob, BlobAndProofV1):
87+
if not isinstance(received_blob, BlobAndProofV1):
88+
raise ValueError("Received blob is not a BlobAndProofV1.")
89+
if expected_blob.blob != received_blob.blob:
90+
raise ValueError("Blob mismatch.")
91+
if expected_blob.proof != received_blob.proof:
92+
raise ValueError("Proof mismatch.")
93+
elif isinstance(expected_blob, BlobAndProofV2):
94+
if not isinstance(received_blob, BlobAndProofV2):
95+
raise ValueError("Received blob is not a BlobAndProofV2.")
96+
if expected_blob.blob != received_blob.blob:
97+
raise ValueError("Blob mismatch.")
98+
if expected_blob.proofs != received_blob.proofs:
99+
raise ValueError("Proofs mismatch.")
100+
else:
101+
raise ValueError(f"Unexpected blob type: {type(expected_blob)}")
102+
103+
eth_rpc.wait_for_transactions(sent_txs)

src/ethereum_test_execution/transaction_post.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class TransactionPost(BaseExecute):
1818
blocks: List[List[Transaction]]
1919
post: Alloc
2020

21-
format_name: ClassVar[str] = "transaction_post"
21+
format_name: ClassVar[str] = "transaction_post_test"
2222
description: ClassVar[str] = (
2323
"Simple transaction sending, then post-check after all transactions are included"
2424
)

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, BlobTransaction
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+
BlobTransaction,
27+
"blob_transaction_test",
28+
"A test that executes a blob transaction",
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 == BlobTransaction:
52+
return BlobTransaction(
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)