Skip to content

Commit

Permalink
feat(fw): EIP-7002: Add withdrawal request types
Browse files Browse the repository at this point in the history
  • Loading branch information
marioevz committed May 24, 2024
1 parent 908f520 commit 7d15a3e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
TestPrivateKey2,
Transaction,
Withdrawal,
WithdrawalRequest,
add_kzg_version,
ceiling_division,
compute_create2_address,
Expand Down Expand Up @@ -111,6 +112,7 @@
"Transaction",
"TransactionException",
"Withdrawal",
"WithdrawalRequest",
"Yul",
"YulCompiler",
"add_kzg_version",
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum_test_tools/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
Storage,
Transaction,
Withdrawal,
WithdrawalRequest,
)

__all__ = (
Expand Down Expand Up @@ -76,6 +77,7 @@
"TestPrivateKey2",
"Transaction",
"Withdrawal",
"WithdrawalRequest",
"ZeroPaddedHexNumber",
"add_kzg_version",
"ceiling_division",
Expand Down
53 changes: 51 additions & 2 deletions src/ethereum_test_tools/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1300,12 +1300,54 @@ class DepositRequest(DepositRequestGeneric[HexNumber]):
pass


class Requests(RootModel[List[DepositRequest]]):
class WithdrawalRequestGeneric(RequestBase, CamelModel, Generic[NumberBoundTypeVar]):
"""
Generic withdrawal request type used as a parent for WithdrawalRequest and
FixtureWithdrawalRequest.
"""

source_address: Address = Address(0)
validator_public_key: BLSPublicKey
amount: NumberBoundTypeVar

@classmethod
def type_byte(cls) -> bytes:
"""
Returns the withdrawal request type.
"""
return b"\1"

def to_serializable_list(self) -> List[Any]:
"""
Returns the deposit's attributes as a list of serializable elements.
"""
return [
self.source_address,
self.validator_public_key,
Uint(self.amount),
]


class WithdrawalRequest(WithdrawalRequestGeneric[HexNumber]):
"""
Withdrawal Request type
"""

pass


class Requests(RootModel[List[DepositRequest | WithdrawalRequest]]):
"""
Requests for the transition tool.
"""

root: List[DepositRequest] = Field(default_factory=list)
root: List[DepositRequest | WithdrawalRequest] = Field(default_factory=list)

def to_serializable_list(self) -> List[Any]:
"""
Returns the requests as a list of serializable elements.
"""
return [r.type_byte() + eth_rlp.encode(r.to_serializable_list()) for r in self.root]

@cached_property
def trie_root(self) -> Hash:
Expand All @@ -1326,6 +1368,12 @@ def deposit_requests(self) -> List[DepositRequest]:
"""
return [d for d in self.root if isinstance(d, DepositRequest)]

def withdrawal_requests(self) -> List[WithdrawalRequest]:
"""
Returns the list of withdrawal requests.
"""
return [w for w in self.root if isinstance(w, WithdrawalRequest)]


# TODO: Move to other file
# Transition tool models
Expand Down Expand Up @@ -1399,6 +1447,7 @@ class Result(CamelModel):
blob_gas_used: HexNumber | None = None
requests_root: Hash | None = None
deposit_requests: List[DepositRequest] | None = None
withdrawal_requests: List[WithdrawalRequest] | None = None


class TransitionToolOutput(CamelModel):
Expand Down
23 changes: 17 additions & 6 deletions src/ethereum_test_tools/spec/blockchain/blockchain_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from ...common import Alloc, EmptyTrieRoot, Environment, Hash, Requests, Transaction, Withdrawal
from ...common.constants import EmptyOmmersRoot
from ...common.json import to_json
from ...common.types import TransitionToolOutput
from ...common.types import DepositRequest, TransitionToolOutput, WithdrawalRequest
from ..base.base_test import BaseFixture, BaseTest, verify_result, verify_transactions
from ..debugging import print_traces
from .types import (
Expand All @@ -27,6 +27,7 @@
FixtureHeader,
FixtureTransaction,
FixtureWithdrawal,
FixtureWithdrawalRequest,
HiveFixture,
InvalidFixtureBlock,
)
Expand Down Expand Up @@ -150,6 +151,7 @@ def make_genesis(
header=genesis,
withdrawals=None if env.withdrawals is None else [],
deposit_requests=[] if fork.header_requests_required(0, 0) else None,
withdrawal_requests=[] if fork.header_requests_required(0, 0) else None,
).with_rlp(
txs=[], requests=Requests() if fork.header_requests_required(0, 0) else None
),
Expand Down Expand Up @@ -255,11 +257,14 @@ def generate_block_data(
# transition tool processing.
header = header.join(block.rlp_modifier)

requests = (
Requests(root=transition_tool_output.result.deposit_requests)
if transition_tool_output.result.deposit_requests is not None
else None
)
requests = None
if fork.header_requests_required(header.number, header.timestamp):
requests_list: List[DepositRequest | WithdrawalRequest] = []
if transition_tool_output.result.deposit_requests is not None:
requests_list += transition_tool_output.result.deposit_requests
if transition_tool_output.result.withdrawal_requests is not None:
requests_list += transition_tool_output.result.withdrawal_requests
requests = Requests(root=requests_list)

if requests is not None and requests.trie_root != header.requests_root:
raise Exception(
Expand Down Expand Up @@ -343,6 +348,12 @@ def make_fixture(
]
if requests is not None
else None,
withdrawal_requests=[
FixtureWithdrawalRequest.from_withdrawal_request(w)
for w in requests.withdrawal_requests()
]
if requests is not None
else None,
).with_rlp(txs=txs, requests=requests)
if block.exception is None:
fixture_blocks.append(fixture_block)
Expand Down
27 changes: 22 additions & 5 deletions src/ethereum_test_tools/spec/blockchain/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
TransactionGeneric,
Withdrawal,
WithdrawalGeneric,
WithdrawalRequest,
WithdrawalRequestGeneric,
)
from ...exceptions import BlockException, ExceptionInstanceOrList, TransactionException
from ..base.base_test import BaseFixture
Expand Down Expand Up @@ -343,7 +345,7 @@ class Block(Header):
"""
List of withdrawals to perform for this block.
"""
requests: List[DepositRequest] | None = None
requests: List[DepositRequest | WithdrawalRequest] | None = None
"""
Custom list of requests to embed in this block.
"""
Expand Down Expand Up @@ -425,6 +427,7 @@ class FixtureExecutionPayload(CamelModel):
transactions: List[Bytes]
withdrawals: List[Withdrawal] | None = None
deposit_requests: List[DepositRequest] | None = None
withdrawal_requests: List[WithdrawalRequest] | None = None

@classmethod
def from_fixture_header(
Expand All @@ -443,6 +446,7 @@ def from_fixture_header(
transactions=[tx.rlp for tx in transactions],
withdrawals=withdrawals,
deposit_requests=requests.deposit_requests() if requests is not None else None,
withdrawal_requests=requests.withdrawal_requests() if requests is not None else None,
)


Expand Down Expand Up @@ -534,7 +538,7 @@ def from_withdrawal(cls, w: WithdrawalGeneric) -> "FixtureWithdrawal":

class FixtureDepositRequest(DepositRequestGeneric[ZeroPaddedHexNumber]):
"""
Structure to represent a single deposit to be processed by the beacon
Structure to represent a single deposit request to be processed by the beacon
chain.
"""

Expand All @@ -546,6 +550,20 @@ def from_deposit_request(cls, d: DepositRequestGeneric) -> "FixtureDepositReques
return cls(**d.model_dump())


class FixtureWithdrawalRequest(WithdrawalRequestGeneric[ZeroPaddedHexNumber]):
"""
Structure to represent a single withdrawal request to be processed by the beacon
chain.
"""

@classmethod
def from_withdrawal_request(cls, d: WithdrawalRequestGeneric) -> "FixtureWithdrawalRequest":
"""
Returns a FixtureWithdrawalRequest from a WithdrawalRequest.
"""
return cls(**d.model_dump())


class FixtureBlockBase(CamelModel):
"""Representation of an Ethereum block within a test Fixture without RLP bytes."""

Expand All @@ -554,6 +572,7 @@ class FixtureBlockBase(CamelModel):
ommers: List[FixtureHeader] = Field(default_factory=list, alias="uncleHeaders")
withdrawals: List[FixtureWithdrawal] | None = None
deposit_requests: List[FixtureDepositRequest] | None = None
withdrawal_requests: List[FixtureWithdrawalRequest] | None = None

@computed_field(alias="blocknumber") # type: ignore[misc]
@cached_property
Expand All @@ -577,9 +596,7 @@ def with_rlp(self, txs: List[Transaction], requests: Requests | None) -> "Fixtur
block.append([w.to_serializable_list() for w in self.withdrawals])

if requests is not None:
block.append(
[r.type_byte() + eth_rlp.encode(r.to_serializable_list()) for r in requests.root]
)
block.append(requests.to_serializable_list())

return FixtureBlock(
**self.model_dump(),
Expand Down

0 comments on commit 7d15a3e

Please sign in to comment.