Skip to content

Commit 9e2f0e4

Browse files
danceratopzmarioevz
authored andcommitted
refactor(tests): update blobhash tests to use the pre_alloc plugin
Remove hardcoded sender and contract addresses and obtain addresses from the `pre` fixture. refactor(test): don't copy sender to Transaction model This fixed the tests when running from pre_alloc addresses generated on a global pytest session scope; otherwise the default `TestAddress` was used. docs: update changelog refactor(tests/cancun): Further refactor blobhash tests
1 parent a42cf66 commit 9e2f0e4

File tree

4 files changed

+360
-568
lines changed

4 files changed

+360
-568
lines changed

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ The output behavior of `fill` has changed ([#1608](https://github.com/ethereum/e
3737

3838
### 🧪 Test Cases
3939

40+
- 🔀 Refactored `BLOBHASH` opcode context tests to use the `pre_alloc` plugin in order to avoid contract and EOA address collisions ([#1637](https://github.com/ethereum/execution-spec-tests/pull/1637)).
41+
4042
## [v4.5.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.5.0) - 2025-05-14
4143

4244
### 💥 Breaking Change

tests/cancun/eip4844_blobs/common.py

Lines changed: 1 addition & 292 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
"""Common constants, classes & functions local to EIP-4844 tests."""
22

33
from dataclasses import dataclass
4-
from typing import List, Literal, Tuple, Union
5-
6-
from ethereum_test_tools import (
7-
Address,
8-
Hash,
9-
TestAddress,
10-
YulCompiler,
11-
add_kzg_version,
12-
compute_create2_address,
13-
compute_create_address,
14-
)
15-
from ethereum_test_tools.vm.opcode import Opcodes as Op
4+
from typing import List, Literal, Tuple
165

176
from .spec import Spec
187

@@ -51,283 +40,3 @@ def blobs_to_transaction_input(
5140
kzg_commitments.append(blob.kzg_commitment)
5241
kzg_proofs.append(blob.kzg_proof)
5342
return (blobs, kzg_commitments, kzg_proofs)
54-
55-
56-
# Random fixed list of blob versioned hashes
57-
random_blob_hashes = add_kzg_version(
58-
[
59-
"0x00b8c5b09810b5fc07355d3da42e2c3a3e200c1d9a678491b7e8e256fc50cc4f",
60-
"0x005b4c8cc4f86aa2d2cf9e9ce97fca704a11a6c20f6b1d6c00a6e15f6d60a6df",
61-
"0x00878f80eaf10be1a6f618e6f8c071b10a6c14d9b89a3bf2a3f3cf2db6c5681d",
62-
"0x004eb72b108d562c639faeb6f8c6f366a28b0381c7d30431117ec8c7bb89f834",
63-
"0x00a9b2a6c3f3f0675b768d49b5f5dc5b5d988f88d55766247ba9e40b125f16bb",
64-
"0x00a4d4cde4aa01e57fb2c880d1d9c778c33bdf85e48ef4c4d4b4de51abccf4ed",
65-
"0x0071c9b8a0c72d38f5e5b5d08e5cb5ce5e23fb1bc5d75f9c29f7b94df0bceeb7",
66-
"0x002c8b6a8b11410c7d98d790e1098f1ed6d93cb7a64711481aaab1848e13212f",
67-
"0x00d78c25f8a1d6aa04d0e2e2a71cf8dfaa4239fa0f301eb57c249d1e6bfe3c3d",
68-
"0x00c778eb1348a73b9c30c7b1d282a5f8b2c5b5a12d5c5e4a4a35f9c5f639b4a4",
69-
],
70-
Spec.BLOB_COMMITMENT_VERSION_KZG,
71-
)
72-
73-
74-
class BlobhashContext:
75-
"""
76-
A utility class for mapping common EVM opcodes in different contexts
77-
to specific bytecode (with BLOBHASH), addresses and contracts.
78-
"""
79-
80-
yul_compiler: Union[YulCompiler, None] = None
81-
addresses = {
82-
"blobhash_sstore": Address(0x100),
83-
"blobhash_return": Address(0x600),
84-
"call": Address(0x200),
85-
"delegatecall": Address(0x300),
86-
"callcode": Address(0x800),
87-
"staticcall": Address(0x700),
88-
"create": Address(0x400),
89-
"create2": Address(0x500),
90-
}
91-
92-
@staticmethod
93-
def _get_blobhash_verbatim():
94-
"""Return BLOBHASH verbatim as a formatted string."""
95-
return "verbatim_{}i_{}o".format(
96-
Op.BLOBHASH.popped_stack_items,
97-
Op.BLOBHASH.pushed_stack_items,
98-
)
99-
100-
@classmethod
101-
def address(cls, context_name):
102-
"""Map opcode context to a specific address."""
103-
address = cls.addresses.get(context_name)
104-
if address is None:
105-
raise ValueError(f"Invalid context: {context_name}")
106-
return address
107-
108-
@classmethod
109-
def code(cls, context_name):
110-
"""Map opcode context to bytecode that utilizes the BLOBHASH opcode."""
111-
assert cls.yul_compiler is not None, "YulCompiler not set"
112-
113-
blobhash_verbatim = cls._get_blobhash_verbatim()
114-
115-
code = {
116-
"blobhash_sstore": cls.yul_compiler(
117-
f"""
118-
{{
119-
let pos := calldataload(0)
120-
let end := calldataload(32)
121-
for {{}} lt(pos, end) {{ pos := add(pos, 1) }}
122-
{{
123-
let blobhash := {blobhash_verbatim}
124-
(hex"{Op.BLOBHASH.hex()}", pos)
125-
sstore(pos, blobhash)
126-
}}
127-
let blobhash := {blobhash_verbatim}
128-
(hex"{Op.BLOBHASH.hex()}", end)
129-
sstore(end, blobhash)
130-
return(0, 0)
131-
}}
132-
""" # noqa: E272, E201, E202
133-
),
134-
"blobhash_return": cls.yul_compiler(
135-
f"""
136-
{{
137-
let pos := calldataload(0)
138-
let blobhash := {blobhash_verbatim}
139-
(hex"{Op.BLOBHASH.hex()}", pos)
140-
mstore(0, blobhash)
141-
return(0, 32)
142-
}}
143-
""" # noqa: E272, E201, E202
144-
),
145-
"call": cls.yul_compiler(
146-
"""
147-
{
148-
calldatacopy(0, 0, calldatasize())
149-
pop(call(gas(), 0x100, 0, 0, calldatasize(), 0, 0))
150-
}
151-
""" # noqa: E272, E201, E202
152-
),
153-
"delegatecall": cls.yul_compiler(
154-
"""
155-
{
156-
calldatacopy(0, 0, calldatasize())
157-
pop(delegatecall(gas(), 0x100, 0, calldatasize(), 0, 0))
158-
}
159-
""" # noqa: E272, E201, E202
160-
),
161-
"callcode": cls.yul_compiler(
162-
f"""
163-
{{
164-
let pos := calldataload(0)
165-
let end := calldataload(32)
166-
for {{ }} lt(pos, end) {{ pos := add(pos, 1) }}
167-
{{
168-
mstore(0, pos)
169-
pop(callcode(gas(),
170-
{cls.address("blobhash_return")}, 0, 0, 32, 0, 32))
171-
let blobhash := mload(0)
172-
sstore(pos, blobhash)
173-
}}
174-
175-
mstore(0, end)
176-
pop(callcode(gas(),
177-
{cls.address("blobhash_return")}, 0, 0, 32, 0, 32))
178-
let blobhash := mload(0)
179-
sstore(end, blobhash)
180-
return(0, 0)
181-
}}
182-
""" # noqa: E272, E201, E202
183-
),
184-
"staticcall": cls.yul_compiler(
185-
f"""
186-
{{
187-
let pos := calldataload(0)
188-
let end := calldataload(32)
189-
for {{ }} lt(pos, end) {{ pos := add(pos, 1) }}
190-
{{
191-
mstore(0, pos)
192-
pop(staticcall(gas(),
193-
{cls.address("blobhash_return")}, 0, 32, 0, 32))
194-
let blobhash := mload(0)
195-
sstore(pos, blobhash)
196-
}}
197-
198-
mstore(0, end)
199-
pop(staticcall(gas(),
200-
{cls.address("blobhash_return")}, 0, 32, 0, 32))
201-
let blobhash := mload(0)
202-
sstore(end, blobhash)
203-
return(0, 0)
204-
}}
205-
""" # noqa: E272, E201, E202
206-
),
207-
"create": cls.yul_compiler(
208-
"""
209-
{
210-
calldatacopy(0, 0, calldatasize())
211-
pop(create(0, 0, calldatasize()))
212-
}
213-
""" # noqa: E272, E201, E202
214-
),
215-
"create2": cls.yul_compiler(
216-
"""
217-
{
218-
calldatacopy(0, 0, calldatasize())
219-
pop(create2(0, 0, calldatasize(), 0))
220-
}
221-
""" # noqa: E272, E201, E202
222-
),
223-
"initcode": cls.yul_compiler(
224-
f"""
225-
{{
226-
for {{ let pos := 0 }} lt(pos, 10) {{ pos := add(pos, 1) }}
227-
{{
228-
let blobhash := {blobhash_verbatim}
229-
(hex"{Op.BLOBHASH.hex()}", pos)
230-
sstore(pos, blobhash)
231-
}}
232-
return(0, 0)
233-
}}
234-
""" # noqa: E272, E201, E202
235-
),
236-
}
237-
code = code.get(context_name)
238-
if code is None:
239-
raise ValueError(f"Invalid context: {context_name}")
240-
return code
241-
242-
@classmethod
243-
def created_contract(cls, context_name):
244-
"""Map contract creation to a specific context to a specific address."""
245-
contract = {
246-
"tx_created_contract": compute_create_address(address=TestAddress, nonce=0),
247-
"create": compute_create_address(
248-
address=cls.address("create"),
249-
nonce=0,
250-
),
251-
"create2": compute_create2_address(
252-
address=cls.address("create2"),
253-
salt=0,
254-
initcode=cls.code("initcode"),
255-
),
256-
}
257-
contract = contract.get(context_name)
258-
if contract is None:
259-
raise ValueError(f"Invalid contract: {context_name}")
260-
return contract
261-
262-
263-
class BlobhashScenario:
264-
"""A utility class for generating blobhash calls."""
265-
266-
@staticmethod
267-
def create_blob_hashes_list(length: int, max_blobs_per_block: int) -> List[List[Hash]]:
268-
"""
269-
Create list of MAX_BLOBS_PER_BLOCK blob hashes
270-
using `random_blob_hashes`.
271-
272-
Cycle over random_blob_hashes to get a large list of
273-
length: MAX_BLOBS_PER_BLOCK * length
274-
-> [0x01, 0x02, 0x03, 0x04, ..., 0x0A, 0x0B, 0x0C, 0x0D]
275-
276-
Then split list into smaller chunks of max_blobs_per_block
277-
-> [[0x01, 0x02, 0x03, 0x04], ..., [0x0a, 0x0b, 0x0c, 0x0d]]
278-
"""
279-
b_hashes = [
280-
random_blob_hashes[i % len(random_blob_hashes)]
281-
for i in range(max_blobs_per_block * length)
282-
]
283-
return [
284-
b_hashes[i : i + max_blobs_per_block]
285-
for i in range(0, len(b_hashes), max_blobs_per_block)
286-
]
287-
288-
@staticmethod
289-
def blobhash_sstore(index: int, max_blobs_per_block: int):
290-
"""
291-
Return BLOBHASH sstore to the given index.
292-
293-
If the index is out of the valid bounds, 0x01 is written
294-
in storage, as we later check it is overwritten by
295-
the BLOBHASH sstore.
296-
"""
297-
invalidity_check = Op.SSTORE(index, 0x01)
298-
if index < 0 or index >= max_blobs_per_block:
299-
return invalidity_check + Op.SSTORE(index, Op.BLOBHASH(index))
300-
return Op.SSTORE(index, Op.BLOBHASH(index))
301-
302-
@classmethod
303-
def generate_blobhash_bytecode(cls, scenario_name: str, max_blobs_per_block: int) -> bytes:
304-
"""Return BLOBHASH bytecode for the given scenario."""
305-
scenarios = {
306-
"single_valid": sum(
307-
cls.blobhash_sstore(i, max_blobs_per_block) for i in range(max_blobs_per_block)
308-
),
309-
"repeated_valid": sum(
310-
sum(cls.blobhash_sstore(i, max_blobs_per_block) for _ in range(10))
311-
for i in range(max_blobs_per_block)
312-
),
313-
"valid_invalid": sum(
314-
cls.blobhash_sstore(i, max_blobs_per_block)
315-
+ cls.blobhash_sstore(max_blobs_per_block, max_blobs_per_block)
316-
+ cls.blobhash_sstore(i, max_blobs_per_block)
317-
for i in range(max_blobs_per_block)
318-
),
319-
"varied_valid": sum(
320-
cls.blobhash_sstore(i, max_blobs_per_block)
321-
+ cls.blobhash_sstore(i + 1, max_blobs_per_block)
322-
+ cls.blobhash_sstore(i, max_blobs_per_block)
323-
for i in range(max_blobs_per_block - 1)
324-
),
325-
"invalid_calls": sum(
326-
cls.blobhash_sstore(i, max_blobs_per_block)
327-
for i in range(-5, max_blobs_per_block + 5)
328-
),
329-
}
330-
scenario = scenarios.get(scenario_name)
331-
if scenario is None:
332-
raise ValueError(f"Invalid scenario: {scenario_name}")
333-
return scenario

0 commit comments

Comments
 (0)