Skip to content

Commit e987972

Browse files
committed
Merge remote-tracking branch 'upstream/eips/amsterdam/eip-7843' into devnets/bal/2
2 parents 6f201d8 + 6f92350 commit e987972

24 files changed

Lines changed: 337 additions & 7 deletions

File tree

packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/simulators/single_test_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ def client_genesis(fixture: BlockchainFixtureCommon) -> dict:
3333
alloc = to_json(fixture.pre)
3434
# NOTE: nethermind requires account keys without '0x' prefix
3535
genesis["alloc"] = {k.replace("0x", ""): v for k, v in alloc.items()}
36+
# NOTE: geth expects slotNumber as plain integer, not hex string
37+
if "slotNumber" in genesis:
38+
genesis["slotNumber"] = int(genesis["slotNumber"], 16)
3639
return genesis
3740

3841

packages/testing/src/execution_testing/fixtures/blockchain.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ class FixtureHeader(CamelModel):
210210
block_access_list_hash: (
211211
Annotated[Hash, HeaderForkRequirement("bal_hash")] | None
212212
) = Field(None, alias="blockAccessListHash")
213+
slot_number: (
214+
Annotated[ZeroPaddedHexNumber, HeaderForkRequirement("slot_number")]
215+
| None
216+
) = Field(None)
213217

214218
fork: Fork | None = Field(None, exclude=True)
215219

@@ -361,7 +365,7 @@ def get_default_from_annotation(
361365
def genesis(cls, fork: Fork, env: Environment, state_root: Hash) -> Self:
362366
"""Get the genesis header for the given fork."""
363367
environment_values = env.model_dump(
364-
exclude_none=True, exclude={"withdrawals"}
368+
exclude_none=True, exclude={"withdrawals", "slot_number"}
365369
)
366370
if env.withdrawals is not None:
367371
environment_values["withdrawals_root"] = Withdrawal.list_root(
@@ -378,6 +382,13 @@ def genesis(cls, fork: Fork, env: Environment, state_root: Hash) -> Self:
378382
if fork.header_bal_hash_required(block_number=0, timestamp=0)
379383
else None
380384
),
385+
"slot_number": (
386+
0
387+
if fork.header_slot_number_required(
388+
block_number=0, timestamp=0
389+
)
390+
else None
391+
),
381392
"fork": fork,
382393
}
383394
return cls(**environment_values, **extras)
@@ -418,6 +429,7 @@ class FixtureExecutionPayload(CamelModel):
418429
block_access_list: Bytes | None = Field(
419430
None, description="RLP-serialized EIP-7928 Block Access List"
420431
)
432+
slot_number: HexNumber | None = Field(None)
421433

422434
@classmethod
423435
def from_fixture_header(

packages/testing/src/execution_testing/forks/base_fork.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ def header_bal_hash_required(
369369
"""Return true if the header must contain block access list hash."""
370370
pass
371371

372+
@classmethod
373+
@abstractmethod
374+
def header_slot_number_required(
375+
cls, *, block_number: int = 0, timestamp: int = 0
376+
) -> bool:
377+
"""Return true if the header must contain slot number (EIP-7843)."""
378+
pass
379+
372380
# Gas related abstract methods
373381

374382
@classmethod

packages/testing/src/execution_testing/forks/forks/forks.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,14 @@ def header_beacon_root_required(
977977
del block_number, timestamp
978978
return False
979979

980+
@classmethod
981+
def header_slot_number_required(
982+
cls, *, block_number: int = 0, timestamp: int = 0
983+
) -> bool:
984+
"""At genesis, header must not contain slot number (EIP-7843)."""
985+
del block_number, timestamp
986+
return False
987+
980988
@classmethod
981989
def engine_new_payload_blob_hashes(
982990
cls, *, block_number: int = 0, timestamp: int = 0
@@ -3362,9 +3370,7 @@ class Amsterdam(BPO2):
33623370
def header_bal_hash_required(
33633371
cls, *, block_number: int = 0, timestamp: int = 0
33643372
) -> bool:
3365-
"""
3366-
From Amsterdam, header must contain block access list hash (EIP-7928).
3367-
"""
3373+
"""BAL hash in header required from Amsterdam (EIP-7928)."""
33683374
del block_number, timestamp
33693375
return True
33703376

@@ -3373,6 +3379,31 @@ def is_deployed(cls) -> bool:
33733379
"""Return True if this fork is deployed."""
33743380
return False
33753381

3382+
@classmethod
3383+
def valid_opcodes(
3384+
cls, *, block_number: int = 0, timestamp: int = 0
3385+
) -> List[Opcodes]:
3386+
"""Add SLOTNUM opcode for Amsterdam (EIP-7843)."""
3387+
return [Opcodes.SLOTNUM] + super(Amsterdam, cls).valid_opcodes(
3388+
block_number=block_number, timestamp=timestamp
3389+
)
3390+
3391+
@classmethod
3392+
def opcode_gas_map(
3393+
cls, *, block_number: int = 0, timestamp: int = 0
3394+
) -> Dict[OpcodeBase, int | Callable[[OpcodeBase], int]]:
3395+
"""Add SLOTNUM opcode gas cost for Amsterdam (EIP-7843)."""
3396+
gas_costs = cls.gas_costs(
3397+
block_number=block_number, timestamp=timestamp
3398+
)
3399+
base_map = super(Amsterdam, cls).opcode_gas_map(
3400+
block_number=block_number, timestamp=timestamp
3401+
)
3402+
return {
3403+
**base_map,
3404+
Opcodes.SLOTNUM: gas_costs.G_BASE,
3405+
}
3406+
33763407
@classmethod
33773408
def engine_new_payload_version(
33783409
cls, *, block_number: int = 0, timestamp: int = 0
@@ -3391,3 +3422,11 @@ def engine_execution_payload_block_access_list(
33913422
"""
33923423
del block_number, timestamp
33933424
return True
3425+
3426+
@classmethod
3427+
def header_slot_number_required(
3428+
cls, *, block_number: int = 0, timestamp: int = 0
3429+
) -> bool:
3430+
"""Slot number in header required from Amsterdam (EIP-7843)."""
3431+
del block_number, timestamp
3432+
return True

packages/testing/src/execution_testing/specs/blockchain.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ class Header(CamelModel):
154154
parent_beacon_block_root: Removable | Hash | None = None
155155
requests_hash: Removable | Hash | None = None
156156
bal_hash: Removable | Hash | None = None
157+
slot_number: Removable | HexNumber | None = None
157158

158159
REMOVE_FIELD: ClassVar[Removable] = Removable()
159160
"""
@@ -327,6 +328,8 @@ def set_environment(self, env: Environment) -> Environment:
327328
and self.block_access_list is not None
328329
):
329330
new_env_values["block_access_list"] = self.block_access_list
331+
if not isinstance(self.slot_number, Removable):
332+
new_env_values["slot_number"] = self.slot_number
330333
"""
331334
These values are required, but they depend on the previous environment,
332335
so they can be calculated here.
@@ -656,6 +659,12 @@ def generate_block_data(
656659
fork=self.fork,
657660
)
658661

662+
# Clear block_access_list_hash if the fork doesn't require it
663+
if not self.fork.header_bal_hash_required(
664+
block_number=int(env.number), timestamp=int(env.timestamp)
665+
):
666+
header.block_access_list_hash = None
667+
659668
if block.header_verify is not None:
660669
# Verify the header after transition tool processing.
661670
try:
@@ -760,8 +769,11 @@ def generate_block_data(
760769
bal = block.expected_block_access_list.modify_if_invalid_test(
761770
t8n_bal
762771
)
763-
if bal != t8n_bal:
764-
# If the BAL was modified, update the header hash
772+
if bal != t8n_bal and self.fork.header_bal_hash_required(
773+
block_number=int(env.number), timestamp=int(env.timestamp)
774+
):
775+
# If the BAL was modified and the fork requires it,
776+
# update the header hash
765777
header.block_access_list_hash = Hash(bal.rlp.keccak256())
766778

767779
built_block = BuiltBlock(

packages/testing/src/execution_testing/specs/state.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ def _generate_blockchain_blocks(self) -> List[Block]:
310310
"extra_data": self.env.extra_data,
311311
"withdrawals": self.env.withdrawals,
312312
"parent_beacon_block_root": self.env.parent_beacon_block_root,
313+
"slot_number": self.env.slot_number,
313314
"txs": [self.tx],
314315
"ommers": [],
315316
"header_verify": self.blockchain_test_header_verify,

packages/testing/src/execution_testing/specs/static_state/environment.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class EnvironmentInStateTestFiller(BaseModel):
3333
current_excess_blob_gas: ValueInFiller | None = Field(
3434
None, alias="currentExcessBlobGas"
3535
)
36+
current_slot_number: ValueInFiller | None = Field(None, alias="slotNumber")
3637

3738
model_config = ConfigDict(extra="forbid")
3839

@@ -72,4 +73,6 @@ def get_environment(self, tags: TagDict) -> Environment:
7273
kwargs["base_fee_per_gas"] = self.current_base_fee
7374
if self.current_excess_blob_gas is not None:
7475
kwargs["excess_blob_gas"] = self.current_excess_blob_gas
76+
if self.current_slot_number is not None:
77+
kwargs["slot_number"] = self.current_slot_number
7578
return Environment(**kwargs)

packages/testing/src/execution_testing/test_types/block_types.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class EnvironmentGeneric(CamelModel, Generic[NumberBoundTypeVar]):
101101
excess_blob_gas: NumberBoundTypeVar | None = Field(
102102
None, alias="currentExcessBlobGas"
103103
)
104+
slot_number: NumberBoundTypeVar | None = Field(None, alias="slotNumber")
104105

105106
parent_difficulty: NumberBoundTypeVar | None = Field(None)
106107
parent_timestamp: NumberBoundTypeVar | None = Field(None)
@@ -223,6 +224,14 @@ def set_fork_requirements(self, fork: Fork) -> "Environment":
223224
):
224225
updated_values["parent_beacon_block_root"] = 0
225226

227+
if (
228+
fork.header_slot_number_required(
229+
block_number=number, timestamp=timestamp
230+
)
231+
and self.slot_number is None
232+
):
233+
updated_values["slot_number"] = 0
234+
226235
return self.copy(**updated_values)
227236

228237
def __hash__(self) -> int:

packages/testing/src/execution_testing/vm/opcodes.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,36 @@ class Opcodes(Opcode, Enum):
22252225
Source: [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516)
22262226
"""
22272227

2228+
SLOTNUM = Opcode(0x4B, popped_stack_items=0, pushed_stack_items=1)
2229+
"""
2230+
SLOTNUM() = slotNumber
2231+
----
2232+
2233+
Description
2234+
----
2235+
Returns the current slot number as provided by the consensus layer.
2236+
The slot number is passed from the consensus layer to the execution
2237+
layer through the engine API.
2238+
2239+
Inputs
2240+
----
2241+
- None
2242+
2243+
Outputs
2244+
----
2245+
- slotNumber: current slot number (uint64)
2246+
2247+
Fork
2248+
----
2249+
Amsterdam
2250+
2251+
Gas
2252+
----
2253+
2
2254+
2255+
Source: [EIP-7843](https://eips.ethereum.org/EIPS/eip-7843)
2256+
"""
2257+
22282258
POP = Opcode(0x50, popped_stack_items=1)
22292259
"""
22302260
POP()

src/ethereum/forks/amsterdam/blocks.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ class Header:
253253
[EIP-7928]: https://eips.ethereum.org/EIPS/eip-7928
254254
[cbalh]: ref:ethereum.forks.amsterdam.block_access_lists.rlp_utils.compute_block_access_list_hash
255255
""" # noqa: E501
256+
slot_number: U64
257+
"""
258+
The slot number of this block as provided by the consensus layer.
259+
Introduced in [EIP-7843].
260+
261+
[EIP-7843]: https://eips.ethereum.org/EIPS/eip-7843
262+
"""
256263

257264

258265
@slotted_freezable

0 commit comments

Comments
 (0)