Skip to content

Commit 148ccca

Browse files
authored
Merge pull request #3998 from jtraglia/exclude-empty-requests
Exclude empty requests in requests list
2 parents e5aba7b + 4aad8eb commit 148ccca

File tree

7 files changed

+234
-29
lines changed

7 files changed

+234
-29
lines changed

specs/electra/beacon-chain.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [Constants](#constants)
1313
- [Misc](#misc)
1414
- [Withdrawal prefixes](#withdrawal-prefixes)
15+
- [Execution layer triggered requests](#execution-layer-triggered-requests)
1516
- [Preset](#preset)
1617
- [Gwei values](#gwei-values)
1718
- [Rewards and penalties](#rewards-and-penalties)
@@ -138,6 +139,14 @@ The following values are (non-configurable) constants used throughout the specif
138139
| - | - |
139140
| `COMPOUNDING_WITHDRAWAL_PREFIX` | `Bytes1('0x02')` |
140141

142+
### Execution layer triggered requests
143+
144+
| Name | Value |
145+
| - | - |
146+
| `DEPOSIT_REQUEST_TYPE` | `Bytes1('0x00')` |
147+
| `WITHDRAWAL_REQUEST_TYPE` | `Bytes1('0x01')` |
148+
| `CONSOLIDATION_REQUEST_TYPE` | `Bytes1('0x02')` |
149+
141150
## Preset
142151

143152
### Gwei values
@@ -1168,11 +1177,17 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
11681177

11691178
```python
11701179
def get_execution_requests_list(execution_requests: ExecutionRequests) -> Sequence[bytes]:
1171-
deposit_bytes = ssz_serialize(execution_requests.deposits)
1172-
withdrawal_bytes = ssz_serialize(execution_requests.withdrawals)
1173-
consolidation_bytes = ssz_serialize(execution_requests.consolidations)
1174-
1175-
return [deposit_bytes, withdrawal_bytes, consolidation_bytes]
1180+
requests = [
1181+
(DEPOSIT_REQUEST_TYPE, execution_requests.deposits),
1182+
(WITHDRAWAL_REQUEST_TYPE, execution_requests.withdrawals),
1183+
(CONSOLIDATION_REQUEST_TYPE, execution_requests.consolidations),
1184+
]
1185+
1186+
return [
1187+
request_type + ssz_serialize(request_data)
1188+
for request_type, request_data in requests
1189+
if len(request_data) != 0
1190+
]
11761191
```
11771192

11781193
##### Modified `process_execution_payload`

specs/electra/validator.md

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,55 @@ def prepare_execution_payload(state: BeaconState,
189189

190190
*[New in Electra]*
191191

192-
1. The execution payload is obtained from the execution engine as defined above using `payload_id`. The response also includes a `execution_requests` entry containing a list of bytes. Each element on the list corresponds to one SSZ list of requests as defined
193-
in [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). The index of each element in the array determines the type of request.
192+
1. The execution payload is obtained from the execution engine as defined above using `payload_id`. The response also includes a `execution_requests` entry containing a list of bytes. Each element on the list corresponds to one SSZ list of requests as defined in [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). The first byte of each request is used to determine the request type. Requests must be ordered by request type in ascending order. As a result, there can only be at most one instance of each request type.
194193
2. Set `block.body.execution_requests = get_execution_requests(execution_requests)`, where:
195194

196195
```python
197-
def get_execution_requests(execution_requests: Sequence[bytes]) -> ExecutionRequests:
198-
deposits = ssz_deserialize(List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD], execution_requests[0])
199-
withdrawals = ssz_deserialize(List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD], execution_requests[1])
200-
consolidations = ssz_deserialize(List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
201-
execution_requests[2])
202-
203-
return ExecutionRequests(deposits, withdrawals, consolidations)
196+
def get_execution_requests(execution_requests_list: Sequence[bytes]) -> ExecutionRequests:
197+
deposits = []
198+
withdrawals = []
199+
consolidations = []
200+
201+
request_types = [
202+
DEPOSIT_REQUEST_TYPE,
203+
WITHDRAWAL_REQUEST_TYPE,
204+
CONSOLIDATION_REQUEST_TYPE,
205+
]
206+
207+
prev_request_type = None
208+
for request in execution_requests_list:
209+
request_type, request_data = request[0:1], request[1:]
210+
211+
# Check that the request type is valid
212+
assert request_type in request_types
213+
# Check that the request data is not empty
214+
assert len(request_data) != 0
215+
# Check that requests are in strictly ascending order
216+
# Each successive type must be greater than the last with no duplicates
217+
assert prev_request_type is None or prev_request_type < request_type
218+
prev_request_type = request_type
219+
220+
if request_type == DEPOSIT_REQUEST_TYPE:
221+
deposits = ssz_deserialize(
222+
List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD],
223+
request_data
224+
)
225+
elif request_type == WITHDRAWAL_REQUEST_TYPE:
226+
withdrawals = ssz_deserialize(
227+
List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD],
228+
request_data
229+
)
230+
elif request_type == CONSOLIDATION_REQUEST_TYPE:
231+
consolidations = ssz_deserialize(
232+
List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD],
233+
request_data
234+
)
235+
236+
return ExecutionRequests(
237+
deposits=deposits,
238+
withdrawals=withdrawals,
239+
consolidations=consolidations,
240+
)
204241
```
205242

206243
## Attesting

tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_blocks.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
get_signed_address_change,
1010
)
1111
from eth2spec.test.helpers.execution_payload import (
12-
compute_el_block_hash,
12+
compute_el_block_hash_for_block,
1313
)
1414
from eth2spec.test.helpers.voluntary_exits import (
1515
prepare_signed_exits,
@@ -42,7 +42,7 @@ def test_basic_el_withdrawal_request(spec, state):
4242
)
4343
block = build_empty_block_for_next_slot(spec, state)
4444
block.body.execution_requests.withdrawals = [withdrawal_request]
45-
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
45+
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
4646
signed_block = state_transition_and_sign_block(spec, state, block)
4747

4848
yield 'blocks', [signed_block]
@@ -80,7 +80,7 @@ def test_basic_btec_and_el_withdrawal_request_in_same_block(spec, state):
8080
)
8181
block.body.execution_requests.withdrawals = [withdrawal_request]
8282

83-
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
83+
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
8484
signed_block = state_transition_and_sign_block(spec, state, block)
8585

8686
yield 'blocks', [signed_block]
@@ -132,7 +132,7 @@ def test_basic_btec_before_el_withdrawal_request(spec, state):
132132
)
133133
block_2 = build_empty_block_for_next_slot(spec, state)
134134
block_2.body.execution_requests.withdrawals = [withdrawal_request]
135-
block_2.body.execution_payload.block_hash = compute_el_block_hash(spec, block_2.body.execution_payload, state)
135+
block_2.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block_2)
136136
signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
137137

138138
yield 'blocks', [signed_block_1, signed_block_2]
@@ -165,7 +165,7 @@ def test_cl_exit_and_el_withdrawal_request_in_same_block(spec, state):
165165
block = build_empty_block_for_next_slot(spec, state)
166166
block.body.voluntary_exits = signed_voluntary_exits
167167
block.body.execution_requests.withdrawals = [withdrawal_request]
168-
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
168+
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
169169
signed_block = state_transition_and_sign_block(spec, state, block)
170170

171171
yield 'blocks', [signed_block]

tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_deposit_transition.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
prepare_deposit_request,
1313
)
1414
from eth2spec.test.helpers.execution_payload import (
15-
compute_el_block_hash,
15+
compute_el_block_hash_for_block,
1616
)
1717
from eth2spec.test.helpers.keys import privkeys, pubkeys
1818
from eth2spec.test.helpers.state import (
@@ -134,7 +134,7 @@ def prepare_state_and_block(spec,
134134
# Assign deposits and deposit requests
135135
block.body.deposits = deposits
136136
block.body.execution_requests.deposits = deposit_requests
137-
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
137+
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
138138

139139
return state, block
140140

@@ -251,7 +251,7 @@ def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
251251
# Artificially assign deposit's pubkey to a deposit request of the same block
252252
top_up_keys = [block.body.deposits[0].data.pubkey]
253253
block.body.execution_requests.deposits[0].pubkey = top_up_keys[0]
254-
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
254+
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
255255

256256
pre_pending_deposits = len(state.pending_deposits)
257257

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from eth2spec.test.context import (
2+
single_phase,
3+
spec_test,
4+
with_electra_and_later,
5+
)
6+
7+
8+
@with_electra_and_later
9+
@spec_test
10+
@single_phase
11+
def test_requests_serialization_round_trip__empty(spec):
12+
execution_requests = spec.ExecutionRequests()
13+
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
14+
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
15+
assert deserialized_execution_requests == execution_requests
16+
17+
18+
@with_electra_and_later
19+
@spec_test
20+
@single_phase
21+
def test_requests_serialization_round_trip__one_request(spec):
22+
execution_requests = spec.ExecutionRequests(
23+
deposits=[spec.DepositRequest()],
24+
)
25+
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
26+
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
27+
assert deserialized_execution_requests == execution_requests
28+
29+
30+
@with_electra_and_later
31+
@spec_test
32+
@single_phase
33+
def test_requests_serialization_round_trip__multiple_requests(spec):
34+
execution_requests = spec.ExecutionRequests(
35+
deposits=[spec.DepositRequest()],
36+
withdrawals=[spec.WithdrawalRequest()],
37+
consolidations=[spec.ConsolidationRequest()],
38+
)
39+
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
40+
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
41+
assert deserialized_execution_requests == execution_requests
42+
43+
44+
@with_electra_and_later
45+
@spec_test
46+
@single_phase
47+
def test_requests_serialization_round_trip__one_request_with_real_data(spec):
48+
execution_requests = spec.ExecutionRequests(
49+
deposits=[
50+
spec.DepositRequest(
51+
pubkey=spec.BLSPubkey(48 * "aa"),
52+
withdrawal_credentials=spec.Bytes32(32 * "bb"),
53+
amount=spec.Gwei(11111111),
54+
signature=spec.BLSSignature(96 * "cc"),
55+
index=spec.uint64(22222222),
56+
),
57+
]
58+
)
59+
serialized_execution_requests = spec.get_execution_requests_list(execution_requests)
60+
deserialized_execution_requests = spec.get_execution_requests(serialized_execution_requests)
61+
assert deserialized_execution_requests == execution_requests
62+
63+
64+
@with_electra_and_later
65+
@spec_test
66+
@single_phase
67+
def test_requests_deserialize__reject_duplicate_request(spec):
68+
serialized_withdrawal = 76 * b"\x0a"
69+
serialized_execution_requests = [
70+
spec.WITHDRAWAL_REQUEST_TYPE + serialized_withdrawal,
71+
spec.WITHDRAWAL_REQUEST_TYPE + serialized_withdrawal,
72+
]
73+
try:
74+
spec.get_execution_requests(serialized_execution_requests)
75+
assert False, "expected exception"
76+
except Exception:
77+
pass
78+
79+
80+
@with_electra_and_later
81+
@spec_test
82+
@single_phase
83+
def test_requests_deserialize__reject_out_of_order_requests(spec):
84+
serialized_execution_requests = [
85+
spec.WITHDRAWAL_REQUEST_TYPE + 76 * b"\x0a",
86+
spec.DEPOSIT_REQUEST_TYPE + 192 * b"\x0b",
87+
]
88+
assert int(serialized_execution_requests[0][0]) > int(serialized_execution_requests[1][0])
89+
try:
90+
spec.get_execution_requests(serialized_execution_requests)
91+
assert False, "expected exception"
92+
except Exception:
93+
pass
94+
95+
96+
@with_electra_and_later
97+
@spec_test
98+
@single_phase
99+
def test_requests_deserialize__reject_empty_request(spec):
100+
serialized_execution_requests = [b"\x01"]
101+
try:
102+
spec.get_execution_requests(serialized_execution_requests)
103+
assert False, "expected exception"
104+
except Exception:
105+
pass
106+
107+
108+
@with_electra_and_later
109+
@spec_test
110+
@single_phase
111+
def test_requests_deserialize__reject_unexpected_request_type(spec):
112+
serialized_execution_requests = [
113+
b"\x03\xff\xff\xff",
114+
]
115+
try:
116+
spec.get_execution_requests(serialized_execution_requests)
117+
assert False, "expected exception"
118+
except Exception:
119+
pass

0 commit comments

Comments
 (0)