Skip to content

Commit

Permalink
feat(fw,tests): framework changes for eip7742.
Browse files Browse the repository at this point in the history
  • Loading branch information
spencer-tb committed Nov 20, 2024
1 parent e546b9c commit 62b5787
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 64 deletions.
63 changes: 38 additions & 25 deletions src/ethereum_test_fixtures/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ class FixtureHeader(CamelModel):
None
)
requests_hash: Annotated[Hash, HeaderForkRequirement("requests")] | None = Field(None)
target_blobs_per_block: Annotated[
ZeroPaddedHexNumber, HeaderForkRequirement("target_blobs_per_block")
] | None = Field(None)

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

Expand Down Expand Up @@ -199,6 +202,8 @@ class FixtureExecutionPayload(CamelModel):
blob_gas_used: HexNumber | None = Field(None)
excess_blob_gas: HexNumber | None = Field(None)

target_blobs_per_block: HexNumber | None = Field(None)

block_hash: Hash

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

EngineNewPayloadV1Parameters = Tuple[FixtureExecutionPayload]
EngineNewPayloadV3Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash]
EngineNewPayloadV4Parameters = Tuple[FixtureExecutionPayload, List[Hash], Hash, List[Bytes]]
EngineNewPayloadV4Parameters = Tuple[
FixtureExecutionPayload,
List[Hash],
Hash,
List[Bytes],
HexNumber,
]

# Important: We check EngineNewPayloadV3Parameters first as it has more fields, and pydantic
# has a weird behavior when the smaller tuple is checked first.
Expand Down Expand Up @@ -286,32 +297,34 @@ def from_fixture_header(
transactions=transactions,
withdrawals=withdrawals,
)
params: EngineNewPayloadParameters
if (
fork.engine_new_payload_requests(header.number, header.timestamp)
and requests is not None
):
parent_beacon_block_root = header.parent_beacon_block_root
assert parent_beacon_block_root is not None
params = (
execution_payload,
Transaction.list_blob_versioned_hashes(transactions),
parent_beacon_block_root,
requests,
)
elif fork.engine_new_payload_blob_hashes(header.number, header.timestamp):
parent_beacon_block_root = header.parent_beacon_block_root
assert parent_beacon_block_root is not None
params = (
execution_payload,
Transaction.list_blob_versioned_hashes(transactions),
parent_beacon_block_root,
)
else:
params = (execution_payload,)

params: List[Any] = [execution_payload]
if fork.engine_new_payload_blob_hashes(header.number, header.timestamp):
blob_hashes = Transaction.list_blob_versioned_hashes(transactions)
if blob_hashes is None:
raise ValueError(f"Blob hashes are required for ${fork}.")
params.append(blob_hashes)

if fork.engine_new_payload_beacon_root(header.number, header.timestamp):
parent_beacon_block_root = header.parent_beacon_block_root
if parent_beacon_block_root is None:
raise ValueError(f"Parent beacon block root is required for ${fork}.")
params.append(parent_beacon_block_root)

if fork.engine_new_payload_requests(header.number, header.timestamp):
if requests is None:
raise ValueError(f"Requests are required for ${fork}.")
params.append(requests)

if fork.engine_new_payload_target_blobs_per_block(header.number, header.timestamp):
target_blobs_per_block = header.target_blobs_per_block
if target_blobs_per_block is None:
raise ValueError(f"Target blobs per block is required for ${fork}.")
params.append(target_blobs_per_block)

payload_params: EngineNewPayloadParameters = tuple(params)
new_payload = cls(
params=params,
params=payload_params,
new_payload_version=new_payload_version,
forkchoice_updated_version=forkchoice_updated_version,
**kwargs,
Expand Down
7 changes: 5 additions & 2 deletions src/ethereum_test_fixtures/tests/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@
excess_blob_gas=18,
parent_beacon_block_root=19,
requests_hash=20,
target_blobs_per_block=10,
),
transactions=[
Transaction(
Expand Down Expand Up @@ -790,7 +791,7 @@
).requests_list
],
],
"forkchoiceUpdatedVersion": "3",
"forkchoiceUpdatedVersion": "4",
"newPayloadVersion": "4",
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
Expand Down Expand Up @@ -947,7 +948,7 @@
],
],
"newPayloadVersion": "4",
"forkchoiceUpdatedVersion": "3",
"forkchoiceUpdatedVersion": "4",
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
},
Expand Down Expand Up @@ -1191,6 +1192,7 @@ def test_json_deserialization(
withdrawals_root=Hash(16),
blob_gas_used=17,
excess_blob_gas=18,
target_blobs_per_block=10,
),
transactions=[
Transaction(
Expand Down Expand Up @@ -1249,6 +1251,7 @@ def test_json_deserialization(
"baseFeePerGas": hex(15),
"blobGasUsed": hex(17),
"excessBlobGas": hex(18),
"targetBlobCount": hex(10),
"blockHash": "0xd90115b7fde329f64335763a446af1"
"50ab67e639281dccdb07a007d18bb80211",
"transactions": [
Expand Down
27 changes: 27 additions & 0 deletions src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,14 @@ def transaction_intrinsic_cost_calculator(
"""
pass

@classmethod
@abstractmethod
def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool:
"""
Returns true if the header must contain target blobs per block.
"""
pass

@classmethod
@abstractmethod
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
Expand All @@ -254,6 +262,14 @@ def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
"""
pass

@classmethod
@abstractmethod
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
"""
Returns the target blobs per block for a given fork.
"""
pass

@classmethod
@abstractmethod
def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int:
Expand Down Expand Up @@ -357,6 +373,17 @@ def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0)
"""
pass

@classmethod
@abstractmethod
def engine_new_payload_target_blobs_per_block(
cls, block_number: int = 0, timestamp: int = 0
) -> bool:
"""
Returns true if the engine api version requires new payload calls to include
target blobs per block.
"""
pass

@classmethod
@abstractmethod
def engine_forkchoice_updated_version(
Expand Down
60 changes: 36 additions & 24 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,9 +546,9 @@ def call_opcodes(
"""
At Homestead, DELEGATECALL opcode was introduced.
"""
return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY),] + super(
Homestead, cls
).call_opcodes(block_number, timestamp)
return [(Opcodes.DELEGATECALL, EVMCodeType.LEGACY)] + super(Homestead, cls).call_opcodes(
block_number, timestamp
)

@classmethod
def valid_opcodes(
Expand Down Expand Up @@ -622,9 +622,9 @@ def call_opcodes(
"""
At Byzantium, STATICCALL opcode was introduced.
"""
return [(Opcodes.STATICCALL, EVMCodeType.LEGACY),] + super(
Byzantium, cls
).call_opcodes(block_number, timestamp)
return [(Opcodes.STATICCALL, EVMCodeType.LEGACY)] + super(Byzantium, cls).call_opcodes(
block_number, timestamp
)

@classmethod
def valid_opcodes(
Expand Down Expand Up @@ -656,9 +656,9 @@ def create_opcodes(
"""
At Constantinople, `CREATE2` opcode is added.
"""
return [(Opcodes.CREATE2, EVMCodeType.LEGACY),] + super(
Constantinople, cls
).create_opcodes(block_number, timestamp)
return [(Opcodes.CREATE2, EVMCodeType.LEGACY)] + super(Constantinople, cls).create_opcodes(
block_number, timestamp
)

@classmethod
def valid_opcodes(
Expand Down Expand Up @@ -938,10 +938,17 @@ def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0)
@classmethod
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
"""
Blobs are enabled started from Cancun.
Blobs are enabled starting from Cancun.
"""
return 2**17

@classmethod
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
"""
Blobs are enabled starting from Cancun, with a static target of 3 blobs.
"""
return 3

@classmethod
def tx_types(cls, block_number: int = 0, timestamp: int = 0) -> List[int]:
"""
Expand Down Expand Up @@ -1177,36 +1184,41 @@ def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
return True

@classmethod
def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) -> bool:
def header_target_blobs_per_block_required(
cls,
block_number: int = 0,
timestamp: int = 0,
) -> bool:
"""
Starting at Prague, new payloads include the requests hash as a parameter.
Prague requires that the execution layer header contains the beacon
chain target blobs per block.
"""
return True

@classmethod
def engine_new_payload_blob_hashes(cls, block_number: int = 0, timestamp: int = 0) -> bool:
def engine_new_payload_requests(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Starting at Prague, new payload directives must contain requests as parameter.
Starting at Prague, new payloads include the requests hash as a parameter.
"""
return True

@classmethod
def engine_new_payload_version(
def engine_new_payload_target_blobs_per_block(
cls, block_number: int = 0, timestamp: int = 0
) -> Optional[int]:
) -> bool:
"""
Starting at Prague, new payload calls must use version 4
Starting at Prague, new payloads include the target blobs per block as a parameter.
"""
return 4
return True

@classmethod
def engine_forkchoice_updated_version(
def engine_new_payload_version(
cls, block_number: int = 0, timestamp: int = 0
) -> Optional[int]:
"""
At Prague, version number of NewPayload and ForkchoiceUpdated diverge.
Starting at Prague, new payload calls must use version 4
"""
return 3
return 4


class CancunEIP7692( # noqa: SC200
Expand All @@ -1224,7 +1236,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo
"""
EOF V1 is supported starting from this fork.
"""
return super(CancunEIP7692, cls,).evm_code_types( # noqa: SC200
return super(CancunEIP7692, cls).evm_code_types( # noqa: SC200
block_number,
timestamp,
) + [EVMCodeType.EOF_V1]
Expand Down Expand Up @@ -1253,7 +1265,7 @@ def create_opcodes(
"""
EOF V1 introduces `EOFCREATE`.
"""
return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1),] + super(
return [(Opcodes.EOFCREATE, EVMCodeType.EOF_V1)] + super(
CancunEIP7692, cls # noqa: SC200
).create_opcodes(block_number, timestamp)

Expand Down Expand Up @@ -1283,7 +1295,7 @@ def evm_code_types(cls, block_number: int = 0, timestamp: int = 0) -> List[EVMCo
"""
EOF V1 is supported starting from Osaka.
"""
return super(Osaka, cls,).evm_code_types(
return super(Osaka, cls).evm_code_types(
block_number,
timestamp,
) + [EVMCodeType.EOF_V1]
Expand Down
4 changes: 4 additions & 0 deletions src/ethereum_test_specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class Header(CamelModel):
excess_blob_gas: Removable | HexNumber | None = None
parent_beacon_block_root: Removable | Hash | None = None
requests_hash: Removable | Hash | None = None
target_blobs_per_block: Removable | HexNumber | None = None

REMOVE_FIELD: ClassVar[Removable] = Removable()
"""
Expand Down Expand Up @@ -359,6 +360,9 @@ def make_genesis(
requests_hash=Requests(max_request_type=fork.max_request_type(0, 0))
if fork.header_requests_required(0, 0)
else None,
target_blobs_per_block=fork.target_blobs_per_block(0, 0)
if fork.header_target_blobs_per_block_required(0, 0)
else None,
fork=fork,
)

Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_types/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ class EnvironmentGeneric(CamelModel, Generic[NumberBoundTypeVar]):
difficulty: NumberBoundTypeVar | None = Field(None, alias="currentDifficulty")
base_fee_per_gas: NumberBoundTypeVar | None = Field(None, alias="currentBaseFee")
excess_blob_gas: NumberBoundTypeVar | None = Field(None, alias="currentExcessBlobGas")
target_blobs_per_block: NumberBoundTypeVar | None = Field(
None,
alias="currentTargetBlobsPerBlock",
)

parent_difficulty: NumberBoundTypeVar | None = Field(None)
parent_timestamp: NumberBoundTypeVar | None = Field(None)
Expand Down Expand Up @@ -474,6 +478,14 @@ def set_fork_requirements(self, fork: Fork) -> "Environment":
):
updated_values["parent_beacon_block_root"] = 0

if (
fork.header_target_blobs_per_block_required(number, timestamp)
and self.target_blobs_per_block is None
):
updated_values["target_blobs_per_block"] = fork.target_blobs_per_block(
number, timestamp
)

return self.copy(**updated_values)


Expand Down
Loading

0 comments on commit 62b5787

Please sign in to comment.