Skip to content

Commit

Permalink
fix(fw): Requests spec update
Browse files Browse the repository at this point in the history
  • Loading branch information
marioevz committed Oct 4, 2024
1 parent dba9a5c commit 80c7410
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 56 deletions.
34 changes: 14 additions & 20 deletions src/ethereum_test_specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,6 @@ def validate_withdrawals_root(cls, value):
return Withdrawal.list_root(value)
return value

@field_validator("requests_hash", mode="before")
@classmethod
def validate_requests_hash(cls, value):
"""
Helper validator to convert a list of requests into the requests hash.
"""
if isinstance(value, list):
return Requests(root=value).hash
return value

def apply(self, target: FixtureHeader) -> FixtureHeader:
"""
Produces a fixture header copy with the set values from the modifier.
Expand Down Expand Up @@ -361,7 +351,9 @@ def make_genesis(
if env.withdrawals is not None
else None,
parent_beacon_block_root=env.parent_beacon_block_root,
requests_hash=Requests(root=[]).hash if fork.header_requests_required(0, 0) else None,
requests_hash=Requests(max_request_type=fork.max_request_type(0, 0))
if fork.header_requests_required(0, 0)
else None,
fork=fork,
)

Expand All @@ -381,7 +373,7 @@ def generate_block_data(
previous_env: Environment,
previous_alloc: Alloc,
eips: Optional[List[int]] = None,
) -> Tuple[FixtureHeader, List[Transaction], Alloc, Environment]:
) -> Tuple[FixtureHeader, List[Transaction], Alloc, Environment, List[Bytes] | None]:
"""
Generate common block data for both make_fixture and make_hive_fixture.
"""
Expand Down Expand Up @@ -475,23 +467,25 @@ def generate_block_data(
assert (
transition_tool_output.result.requests is not None
), "Requests are required for this block"
requests = transition_tool_output.result.requests
requests = Requests(requests_list=transition_tool_output.result.requests)

if requests is not None and requests.hash != header.requests_hash:
if requests is not None and Hash(requests) != header.requests_hash:
raise Exception(
f"Requests root in header does not match the requests root in the transition tool "
"Requests root in header does not match the requests root in the transition tool "
"output: "
f"{header.requests_hash} != {requests.hash}"
f"{header.requests_hash} != {Hash(requests)}"
)

if block.requests is not None:
header.requests_hash = Requests(root=[Bytes(r) for r in block.requests]).hash
requests = Requests(requests_list=block.requests)
header.requests_hash = Hash(requests)

return (
header,
txs,
transition_tool_output.alloc,
env,
requests.requests_list if requests is not None else None,
)

def network_info(self, fork: Fork, eips: Optional[List[int]] = None):
Expand Down Expand Up @@ -536,7 +530,7 @@ def make_fixture(
# This is the most common case, the RLP needs to be constructed
# based on the transactions to be included in the block.
# Set the environment according to the block to execute.
header, txs, new_alloc, new_env = self.generate_block_data(
header, txs, new_alloc, new_env, requests = self.generate_block_data(
t8n=t8n,
fork=fork,
block=block,
Expand Down Expand Up @@ -611,7 +605,7 @@ def make_hive_fixture(
head_hash = genesis.header.block_hash

for block in self.blocks:
header, txs, new_alloc, new_env = self.generate_block_data(
header, txs, new_alloc, new_env, requests = self.generate_block_data(
t8n=t8n, fork=fork, block=block, previous_env=env, previous_alloc=alloc, eips=eips
)
if block.rlp is None:
Expand Down Expand Up @@ -647,7 +641,7 @@ def make_hive_fixture(
# Most clients require the header to start the sync process, so we create an empty
# block on top of the last block of the test to send it as new payload and trigger the
# sync process.
sync_header, _, _, _ = self.generate_block_data(
sync_header, _, _, _, requests = self.generate_block_data(
t8n=t8n,
fork=fork,
block=Block(),
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
DepositRequest,
Environment,
Removable,
Requests,
Storage,
TestParameterGroup,
Transaction,
Expand Down Expand Up @@ -124,6 +125,7 @@
"ReferenceSpec",
"ReferenceSpecTypes",
"Removable",
"Requests",
"EOA",
"StateTest",
"StateTestFiller",
Expand Down
72 changes: 37 additions & 35 deletions src/ethereum_test_types/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from abc import abstractmethod
from dataclasses import dataclass
from functools import cached_property
from typing import Any, ClassVar, Dict, Generic, List, Literal, Sequence, Tuple
from typing import Any, ClassVar, Dict, Generic, List, Literal, Sequence, SupportsBytes, Tuple

from coincurve.keys import PrivateKey, PublicKey
from ethereum import rlp as eth_rlp
Expand All @@ -18,9 +18,7 @@
ConfigDict,
Field,
PrivateAttr,
RootModel,
computed_field,
field_validator,
model_serializer,
model_validator,
)
Expand Down Expand Up @@ -1200,49 +1198,53 @@ def __bytes__(self) -> bytes:
return bytes(self.source_address) + bytes(self.source_pubkey) + bytes(self.target_pubkey)


class Requests(RootModel[List[Bytes]]):
def requests_list_to_bytes(requests_list: List[RequestBase] | Bytes | SupportsBytes) -> Bytes:
"""
Converts a list of requests to bytes.
"""
if not isinstance(requests_list, list):
return Bytes(requests_list)
return Bytes(b"".join([bytes(r) for r in requests_list]))


class Requests:
"""
Requests for the transition tool.
"""

root: List[Bytes] = Field(default_factory=list)
requests_list: List[Bytes]

@field_validator("root", mode="before")
@classmethod
def validate_root(cls, value):
def __init__(
self,
*requests: RequestBase,
max_request_type: int | None = None,
requests_list: List[List[RequestBase] | Bytes] | None = None,
):
"""
Helper validator flatten lists contained in the root.
Initializes the requests object.
"""
if isinstance(value, list):
for i, r in enumerate(value):
if isinstance(r, list):
value[i] = b"".join(r)
return value
if requests_list is not None:
assert len(requests) == 0, "requests must be empty if list is provided"
self.requests_list = []
for rl in requests_list:
self.requests_list.append(requests_list_to_bytes(rl))
return

def __bytes__(self) -> bytes:
"""
Returns the requests as bytes.
"""
return b"".join(r for r in self.root)
assert max_request_type is not None, "max_request_type must be provided"

@cached_property
def hash(self) -> Hash:
requests_lists: List[List[RequestBase]] = [[] for _ in range(max_request_type + 1)]
for r in requests:
requests_lists[r.type].append(r)

self.requests_list = [
requests_list_to_bytes(requests_list) for requests_list in requests_lists
]

def __bytes__(self) -> bytes:
"""
Returns the root hash of the requests.
Returns the requests hash.
"""
s: bytes = b""
for r in self.root:
for r in self.requests_list:
s = s + r.sha256()
return Bytes(s).sha256()

@classmethod
def from_list(cls, *requests: RequestBase, max_type_index: int) -> "Requests":
"""
Creates a Requests object from a single list of request objects, automatically
creating lists for each request type, up to the given max_type_index.
"""
root: List[List[RequestBase]] = [[] for _ in range(max_type_index + 1)]
for r in requests:
root[r.type].append(r)

return cls(root=root) # type: ignore
2 changes: 1 addition & 1 deletion src/evm_transition_tool/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class Result(CamelModel):
excess_blob_gas: HexNumber | None = Field(None, alias="currentExcessBlobGas")
blob_gas_used: HexNumber | None = None
requests_hash: Hash | None = None
requests: Requests | None = None
requests: List[Bytes] | None = None


class TransitionToolInput(CamelModel):
Expand Down

0 comments on commit 80c7410

Please sign in to comment.