Skip to content

Commit 62b5787

Browse files
committed
feat(fw,tests): framework changes for eip7742.
1 parent e546b9c commit 62b5787

File tree

8 files changed

+144
-64
lines changed

8 files changed

+144
-64
lines changed

src/ethereum_test_fixtures/blockchain.py

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ class FixtureHeader(CamelModel):
111111
None
112112
)
113113
requests_hash: Annotated[Hash, HeaderForkRequirement("requests")] | None = Field(None)
114+
target_blobs_per_block: Annotated[
115+
ZeroPaddedHexNumber, HeaderForkRequirement("target_blobs_per_block")
116+
] | None = Field(None)
114117

115118
fork: Fork | None = Field(None, exclude=True)
116119

@@ -199,6 +202,8 @@ class FixtureExecutionPayload(CamelModel):
199202
blob_gas_used: HexNumber | None = Field(None)
200203
excess_blob_gas: HexNumber | None = Field(None)
201204

205+
target_blobs_per_block: HexNumber | None = Field(None)
206+
202207
block_hash: Hash
203208

204209
transactions: List[Bytes]
@@ -224,7 +229,13 @@ def from_fixture_header(
224229

225230
EngineNewPayloadV1Parameters = Tuple[FixtureExecutionPayload]
226231
EngineNewPayloadV3Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash]
227-
EngineNewPayloadV4Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash, List[Bytes]]
232+
EngineNewPayloadV4Parameters = Tuple[
233+
FixtureExecutionPayload,
234+
List[Hash],
235+
Hash,
236+
List[Bytes],
237+
HexNumber,
238+
]
228239

229240
# Important: We check EngineNewPayloadV3Parameters first as it has more fields, and pydantic
230241
# has a weird behavior when the smaller tuple is checked first.
@@ -286,32 +297,34 @@ def from_fixture_header(
286297
transactions=transactions,
287298
withdrawals=withdrawals,
288299
)
289-
params: EngineNewPayloadParameters
290-
if (
291-
fork.engine_new_payload_requests(header.number, header.timestamp)
292-
and requests is not None
293-
):
294-
parent_beacon_block_root = header.parent_beacon_block_root
295-
assert parent_beacon_block_root is not None
296-
params = (
297-
execution_payload,
298-
Transaction.list_blob_versioned_hashes(transactions),
299-
parent_beacon_block_root,
300-
requests,
301-
)
302-
elif fork.engine_new_payload_blob_hashes(header.number, header.timestamp):
303-
parent_beacon_block_root = header.parent_beacon_block_root
304-
assert parent_beacon_block_root is not None
305-
params = (
306-
execution_payload,
307-
Transaction.list_blob_versioned_hashes(transactions),
308-
parent_beacon_block_root,
309-
)
310-
else:
311-
params = (execution_payload,)
312300

301+
params: List[Any] = [execution_payload]
302+
if fork.engine_new_payload_blob_hashes(header.number, header.timestamp):
303+
blob_hashes = Transaction.list_blob_versioned_hashes(transactions)
304+
if blob_hashes is None:
305+
raise ValueError(f"Blob hashes are required for ${fork}.")
306+
params.append(blob_hashes)
307+
308+
if fork.engine_new_payload_beacon_root(header.number, header.timestamp):
309+
parent_beacon_block_root = header.parent_beacon_block_root
310+
if parent_beacon_block_root is None:
311+
raise ValueError(f"Parent beacon block root is required for ${fork}.")
312+
params.append(parent_beacon_block_root)
313+
314+
if fork.engine_new_payload_requests(header.number, header.timestamp):
315+
if requests is None:
316+
raise ValueError(f"Requests are required for ${fork}.")
317+
params.append(requests)
318+
319+
if fork.engine_new_payload_target_blobs_per_block(header.number, header.timestamp):
320+
target_blobs_per_block = header.target_blobs_per_block
321+
if target_blobs_per_block is None:
322+
raise ValueError(f"Target blobs per block is required for ${fork}.")
323+
params.append(target_blobs_per_block)
324+
325+
payload_params: EngineNewPayloadParameters = tuple(params)
313326
new_payload = cls(
314-
params=params,
327+
params=payload_params,
315328
new_payload_version=new_payload_version,
316329
forkchoice_updated_version=forkchoice_updated_version,
317330
**kwargs,

src/ethereum_test_fixtures/tests/test_blockchain.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@
668668
excess_blob_gas=18,
669669
parent_beacon_block_root=19,
670670
requests_hash=20,
671+
target_blobs_per_block=10,
671672
),
672673
transactions=[
673674
Transaction(
@@ -790,7 +791,7 @@
790791
).requests_list
791792
],
792793
],
793-
"forkchoiceUpdatedVersion": "3",
794+
"forkchoiceUpdatedVersion": "4",
794795
"newPayloadVersion": "4",
795796
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
796797
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
@@ -947,7 +948,7 @@
947948
],
948949
],
949950
"newPayloadVersion": "4",
950-
"forkchoiceUpdatedVersion": "3",
951+
"forkchoiceUpdatedVersion": "4",
951952
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
952953
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
953954
},
@@ -1191,6 +1192,7 @@ def test_json_deserialization(
11911192
withdrawals_root=Hash(16),
11921193
blob_gas_used=17,
11931194
excess_blob_gas=18,
1195+
target_blobs_per_block=10,
11941196
),
11951197
transactions=[
11961198
Transaction(
@@ -1249,6 +1251,7 @@ def test_json_deserialization(
12491251
"baseFeePerGas": hex(15),
12501252
"blobGasUsed": hex(17),
12511253
"excessBlobGas": hex(18),
1254+
"targetBlobCount": hex(10),
12521255
"blockHash": "0xd90115b7fde329f64335763a446af1"
12531256
"50ab67e639281dccdb07a007d18bb80211",
12541257
"transactions": [

src/ethereum_test_forks/base_fork.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,14 @@ def transaction_intrinsic_cost_calculator(
246246
"""
247247
pass
248248

249+
@classmethod
250+
@abstractmethod
251+
def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool:
252+
"""
253+
Returns true if the header must contain target blobs per block.
254+
"""
255+
pass
256+
249257
@classmethod
250258
@abstractmethod
251259
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
@@ -254,6 +262,14 @@ def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
254262
"""
255263
pass
256264

265+
@classmethod
266+
@abstractmethod
267+
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
268+
"""
269+
Returns the target blobs per block for a given fork.
270+
"""
271+
pass
272+
257273
@classmethod
258274
@abstractmethod
259275
def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int:
@@ -357,6 +373,17 @@ def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0)
357373
"""
358374
pass
359375

376+
@classmethod
377+
@abstractmethod
378+
def engine_new_payload_target_blobs_per_block(
379+
cls, block_number: int = 0, timestamp: int = 0
380+
) -> bool:
381+
"""
382+
Returns true if the engine api version requires new payload calls to include
383+
target blobs per block.
384+
"""
385+
pass
386+
360387
@classmethod
361388
@abstractmethod
362389
def engine_forkchoice_updated_version(

src/ethereum_test_forks/forks/forks.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,9 @@ def call_opcodes(
546546
"""
547547
At Homestead, DELEGATECALL opcode was introduced.
548548
"""
549-
return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY),] + super(
550-
Homestead, cls
551-
).call_opcodes(block_number, timestamp)
549+
return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY)] + super(Homestead, cls).call_opcodes(
550+
block_number, timestamp
551+
)
552552

553553
@classmethod
554554
def valid_opcodes(
@@ -622,9 +622,9 @@ def call_opcodes(
622622
"""
623623
At Byzantium, STATICCALL opcode was introduced.
624624
"""
625-
return [(Opcodes.STATICCALL, EVMCodeType.LEGACY),] + super(
626-
Byzantium, cls
627-
).call_opcodes(block_number, timestamp)
625+
return [(Opcodes.STATICCALL, EVMCodeType.LEGACY)] + super(Byzantium, cls).call_opcodes(
626+
block_number, timestamp
627+
)
628628

629629
@classmethod
630630
def valid_opcodes(
@@ -656,9 +656,9 @@ def create_opcodes(
656656
"""
657657
At Constantinople, `CREATE2` opcode is added.
658658
"""
659-
return [(Opcodes.CREATE2, EVMCodeType.LEGACY),] + super(
660-
Constantinople, cls
661-
).create_opcodes(block_number, timestamp)
659+
return [(Opcodes.CREATE2, EVMCodeType.LEGACY)] + super(Constantinople, cls).create_opcodes(
660+
block_number, timestamp
661+
)
662662

663663
@classmethod
664664
def valid_opcodes(
@@ -938,10 +938,17 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0)
938938
@classmethod
939939
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
940940
"""
941-
Blobs are enabled started from Cancun.
941+
Blobs are enabled starting from Cancun.
942942
"""
943943
return 2**17
944944

945+
@classmethod
946+
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
947+
"""
948+
Blobs are enabled starting from Cancun, with a static target of 3 blobs.
949+
"""
950+
return 3
951+
945952
@classmethod
946953
def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
947954
"""
@@ -1177,36 +1184,41 @@ def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
11771184
return True
11781185

11791186
@classmethod
1180-
def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) -> bool:
1187+
def header_target_blobs_per_block_required(
1188+
cls,
1189+
block_number: int = 0,
1190+
timestamp: int = 0,
1191+
) -> bool:
11811192
"""
1182-
Starting at Prague, new payloads include the requests hash as a parameter.
1193+
Prague requires that the execution layer header contains the beacon
1194+
chain target blobs per block.
11831195
"""
11841196
return True
11851197

11861198
@classmethod
1187-
def engine_new_payload_blob_hashes(cls, block_number: int = 0, timestamp: int = 0) -> bool:
1199+
def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) -> bool:
11881200
"""
1189-
Starting at Prague, new payload directives must contain requests as parameter.
1201+
Starting at Prague, new payloads include the requests hash as a parameter.
11901202
"""
11911203
return True
11921204

11931205
@classmethod
1194-
def engine_new_payload_version(
1206+
def engine_new_payload_target_blobs_per_block(
11951207
cls, block_number: int = 0, timestamp: int = 0
1196-
) -> Optional[int]:
1208+
) -> bool:
11971209
"""
1198-
Starting at Prague, new payload calls must use version 4
1210+
Starting at Prague, new payloads include the target blobs per block as a parameter.
11991211
"""
1200-
return 4
1212+
return True
12011213

12021214
@classmethod
1203-
def engine_forkchoice_updated_version(
1215+
def engine_new_payload_version(
12041216
cls, block_number: int = 0, timestamp: int = 0
12051217
) -> Optional[int]:
12061218
"""
1207-
At Prague, version number of NewPayload and ForkchoiceUpdated diverge.
1219+
Starting at Prague, new payload calls must use version 4
12081220
"""
1209-
return 3
1221+
return 4
12101222

12111223

12121224
class CancunEIP7692( # noqa: SC200
@@ -1224,7 +1236,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo
12241236
"""
12251237
EOF V1 is supported starting from this fork.
12261238
"""
1227-
return super(CancunEIP7692, cls,).evm_code_types( # noqa: SC200
1239+
return super(CancunEIP7692, cls).evm_code_types( # noqa: SC200
12281240
block_number,
12291241
timestamp,
12301242
) + [EVMCodeType.EOF_V1]
@@ -1253,7 +1265,7 @@ def create_opcodes(
12531265
"""
12541266
EOF V1 introduces `EOFCREATE`.
12551267
"""
1256-
return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1),] + super(
1268+
return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1)] + super(
12571269
CancunEIP7692, cls # noqa: SC200
12581270
).create_opcodes(block_number, timestamp)
12591271

@@ -1283,7 +1295,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo
12831295
"""
12841296
EOF V1 is supported starting from Osaka.
12851297
"""
1286-
return super(Osaka, cls,).evm_code_types(
1298+
return super(Osaka, cls).evm_code_types(
12871299
block_number,
12881300
timestamp,
12891301
) + [EVMCodeType.EOF_V1]

src/ethereum_test_specs/blockchain.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class Header(CamelModel):
119119
excess_blob_gas: Removable | HexNumber | None = None
120120
parent_beacon_block_root: Removable | Hash | None = None
121121
requests_hash: Removable | Hash | None = None
122+
target_blobs_per_block: Removable | HexNumber | None = None
122123

123124
REMOVE_FIELD: ClassVar[Removable] = Removable()
124125
"""
@@ -359,6 +360,9 @@ def make_genesis(
359360
requests_hash=Requests(max_request_type=fork.max_request_type(0, 0))
360361
if fork.header_requests_required(0, 0)
361362
else None,
363+
target_blobs_per_block=fork.target_blobs_per_block(0, 0)
364+
if fork.header_target_blobs_per_block_required(0, 0)
365+
else None,
362366
fork=fork,
363367
)
364368

src/ethereum_test_types/types.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ class EnvironmentGeneric(CamelModel, Generic[NumberBoundTypeVar]):
389389
difficulty: NumberBoundTypeVar | None = Field(None, alias="currentDifficulty")
390390
base_fee_per_gas: NumberBoundTypeVar | None = Field(None, alias="currentBaseFee")
391391
excess_blob_gas: NumberBoundTypeVar | None = Field(None, alias="currentExcessBlobGas")
392+
target_blobs_per_block: NumberBoundTypeVar | None = Field(
393+
None,
394+
alias="currentTargetBlobsPerBlock",
395+
)
392396

393397
parent_difficulty: NumberBoundTypeVar | None = Field(None)
394398
parent_timestamp: NumberBoundTypeVar | None = Field(None)
@@ -474,6 +478,14 @@ def set_fork_requirements(self, fork: Fork) -> "Environment":
474478
):
475479
updated_values["parent_beacon_block_root"] = 0
476480

481+
if (
482+
fork.header_target_blobs_per_block_required(number, timestamp)
483+
and self.target_blobs_per_block is None
484+
):
485+
updated_values["target_blobs_per_block"] = fork.target_blobs_per_block(
486+
number, timestamp
487+
)
488+
477489
return self.copy(**updated_values)
478490

479491

0 commit comments

Comments
 (0)