11
11
from dataclasses import dataclass
12
12
from typing import Tuple , Union
13
13
14
+ from ethereum .exceptions import InvalidBlock
15
+
16
+ from .. import rlp
14
17
from ..base_types import (
15
18
U64 ,
16
19
U256 ,
17
20
Bytes ,
18
21
Bytes8 ,
19
22
Bytes32 ,
23
+ Bytes48 ,
24
+ Bytes96 ,
20
25
Uint ,
21
26
slotted_freezable ,
22
27
)
23
28
from ..crypto .hash import Hash32
24
29
from .fork_types import Address , Bloom , Root
25
30
from .transactions import LegacyTransaction
31
+ from .utils .hexadecimal import hex_to_address
32
+
33
+ DEPOSIT_CONTRACT_ADDRESS = hex_to_address (
34
+ "0x00000000219ab540356cbb839cbe05303d7705fa"
35
+ )
36
+ DEPOSIT_REQUEST_TYPE = b"\x00 "
26
37
27
38
28
39
@slotted_freezable
@@ -65,6 +76,7 @@ class Header:
65
76
blob_gas_used : U64
66
77
excess_blob_gas : U64
67
78
parent_beacon_block_root : Root
79
+ requests_root : Root
68
80
69
81
70
82
@slotted_freezable
@@ -78,6 +90,7 @@ class Block:
78
90
transactions : Tuple [Union [Bytes , LegacyTransaction ], ...]
79
91
ommers : Tuple [Header , ...]
80
92
withdrawals : Tuple [Withdrawal , ...]
93
+ requests : Tuple [Bytes , ...]
81
94
82
95
83
96
@slotted_freezable
@@ -103,3 +116,86 @@ class Receipt:
103
116
cumulative_gas_used : Uint
104
117
bloom : Bloom
105
118
logs : Tuple [Log , ...]
119
+
120
+
121
+ def validate_requests (requests : Tuple [Bytes , ...]) -> None :
122
+ """
123
+ Validate a list of requests.
124
+ """
125
+ current_request_type = b"\x00 "
126
+ for request in requests :
127
+ request_type = request [:1 ]
128
+
129
+ # Ensure that no undefined requests are present.
130
+ if request_type != DEPOSIT_REQUEST_TYPE :
131
+ raise InvalidBlock ("BlockException.INVALID_REQUESTS" )
132
+
133
+ # Ensure that requests are in order.
134
+ if request_type < current_request_type :
135
+ raise InvalidBlock ("BlockException.INVALID_REQUESTS" )
136
+ current_request_type = request_type
137
+
138
+
139
+ @slotted_freezable
140
+ @dataclass
141
+ class DepositRequest :
142
+ """
143
+ Requests for validator deposits on chain (See EIP-6110).
144
+ """
145
+
146
+ pubkey : Bytes48
147
+ withdrawal_credentials : Bytes32
148
+ amount : U64
149
+ signature : Bytes96
150
+ index : U64
151
+
152
+
153
+ Request = Union [DepositRequest ]
154
+
155
+
156
+ def encode_request (req : Request ) -> Bytes :
157
+ """
158
+ Encode a request.
159
+ """
160
+ if isinstance (req , DepositRequest ):
161
+ return DEPOSIT_REQUEST_TYPE + rlp .encode (req )
162
+ else :
163
+ raise Exception ("Unknown request type" )
164
+
165
+
166
+ def parse_deposit_data (data : Bytes ) -> Bytes :
167
+ """
168
+ Parses Deposit Request from the DepositContract.DepositEvent data.
169
+ """
170
+ deposit_request = DepositRequest (
171
+ pubkey = Bytes48 (data [192 :240 ]),
172
+ withdrawal_credentials = Bytes32 (data [288 :320 ]),
173
+ amount = U64 .from_le_bytes (data [352 :360 ]),
174
+ signature = Bytes96 (data [416 :512 ]),
175
+ index = U64 .from_le_bytes (data [544 :552 ]),
176
+ )
177
+
178
+ return encode_request (deposit_request )
179
+
180
+
181
+ def validate_deposit_requests (
182
+ receipts : Tuple [Receipt , ...], requests : Tuple [Bytes , ...]
183
+ ) -> None :
184
+ """
185
+ Validate a list of deposit requests.
186
+ """
187
+ # Retrieve all deposit requests from receipts.
188
+ expected_deposit_requests = []
189
+ for receipt in receipts :
190
+ for log in receipt .logs :
191
+ if log .address == DEPOSIT_CONTRACT_ADDRESS :
192
+ deposit_request_rlp = parse_deposit_data (log .data )
193
+ expected_deposit_requests .append (deposit_request_rlp )
194
+
195
+ # Retrieve all deposit requests from block body
196
+ deposit_requests = [
197
+ req for req in requests if req [:1 ] == DEPOSIT_REQUEST_TYPE
198
+ ]
199
+
200
+ if deposit_requests != expected_deposit_requests :
201
+ raise InvalidBlock ("BlockException.INVALID_REQUESTS" )
0 commit comments