Skip to content

Commit e9a5740

Browse files
committed
added pseudo contract that just returns withdrawals requests
1 parent d2519c4 commit e9a5740

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

tests/prague/eip7002_el_triggerable_withdrawals/test_modified_withdrawal_contract.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
Alloc,
1414
Block,
1515
BlockchainTestFiller,
16+
Bytecode,
1617
Transaction,
1718
)
19+
from ethereum_test_tools import Opcodes as Op
1820
from ethereum_test_types import Requests
1921

2022
from .helpers import (
@@ -41,6 +43,26 @@ def single_withdrawal_with_custom_fee(i: int) -> WithdrawalRequest: # noqa: D10
4143
@pytest.mark.parametrize(
4244
"requests_list",
4345
[
46+
pytest.param(
47+
[],
48+
id="empty_request_list",
49+
),
50+
pytest.param(
51+
[single_withdrawal_with_custom_fee(0)],
52+
id="1_withdrawal_request",
53+
),
54+
pytest.param(
55+
[
56+
*[
57+
single_withdrawal_with_custom_fee(i)
58+
for i in range(
59+
0,
60+
15,
61+
)
62+
],
63+
],
64+
id="15_withdrawal_requests",
65+
),
4466
pytest.param(
4567
[
4668
*[
@@ -109,3 +131,126 @@ def test_extra_withdrawals(
109131
],
110132
post={},
111133
)
134+
135+
136+
@pytest.mark.parametrize(
137+
"requests_list",
138+
[
139+
pytest.param(
140+
[],
141+
id="empty_request_list",
142+
),
143+
pytest.param(
144+
[single_withdrawal_with_custom_fee(0)],
145+
id="1_withdrawal_request",
146+
),
147+
pytest.param(
148+
[
149+
*[
150+
single_withdrawal_with_custom_fee(i)
151+
for i in range(
152+
0,
153+
15,
154+
)
155+
],
156+
],
157+
id="15_withdrawal_requests",
158+
),
159+
pytest.param(
160+
[
161+
*[
162+
single_withdrawal_with_custom_fee(i)
163+
for i in range(
164+
0,
165+
16,
166+
)
167+
],
168+
],
169+
id="16_withdrawal_requests",
170+
),
171+
pytest.param(
172+
[
173+
*[
174+
single_withdrawal_with_custom_fee(i)
175+
for i in range(
176+
0,
177+
17,
178+
)
179+
],
180+
],
181+
id="17_withdrawal_requests",
182+
),
183+
pytest.param(
184+
[
185+
*[
186+
single_withdrawal_with_custom_fee(i)
187+
for i in range(
188+
0,
189+
18,
190+
)
191+
],
192+
],
193+
id="18_withdrawal_requests",
194+
),
195+
],
196+
)
197+
def test_extra_withdrawals_pseudo_contract(
198+
blockchain_test: BlockchainTestFiller,
199+
pre: Alloc,
200+
requests_list: List[WithdrawalRequest],
201+
):
202+
"""Test how clients were to behave when more than 16 withdrawals would be allowed per block."""
203+
modified_code: Bytecode = Bytecode()
204+
memory_offset: int = 0
205+
206+
# Goal: Have contract return a bunch of withdrawal requests
207+
# Problem: EVM has no concept of withdrawal request, it just return bytes from memory
208+
# Problem: How to get __bytes__ from withdrawal request
209+
# Problem: How to know exact size of withdrawal requests byte representation
210+
# Problem: If size larger than 32 bytes how to split across multiple MSTOREs?
211+
212+
# what size does a withdrawal_request bytes representation have?
213+
# withdrawal_request.__bytes__:
214+
# bytes(self.source_address) -> 20 bytes
215+
# + bytes(self.validator_pubkey) -> 48 bytes
216+
# + self.amount.to_bytes(8, "little") -> 8 bytes
217+
# -> Total: 76 bytes
218+
for withdrawal_request in requests_list:
219+
withdrawal_request_chunk_1_3_32bytes: bytes = withdrawal_request.__bytes__()[:32]
220+
withdrawal_request_chunk_2_3_32bytes: bytes = withdrawal_request.__bytes__()[32:64]
221+
withdrawal_request_chunk_3_3_12bytes: bytes = withdrawal_request.__bytes__()[64:]
222+
223+
modified_code += Op.MSTORE(memory_offset, withdrawal_request_chunk_1_3_32bytes)
224+
memory_offset += 32
225+
226+
modified_code += Op.MSTORE(memory_offset, withdrawal_request_chunk_2_3_32bytes)
227+
memory_offset += 32
228+
229+
modified_code += Op.MSTORE(memory_offset, withdrawal_request_chunk_3_3_12bytes)
230+
memory_offset += 12
231+
232+
modified_code += Op.RETURN(0, 76)
233+
234+
pre[Spec_EIP7002.WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS] = Account(
235+
code=modified_code,
236+
nonce=1,
237+
balance=0,
238+
)
239+
240+
# given a list of withdrawal requests construct a withdrawal request transaction
241+
withdrawal_request_transaction = WithdrawalRequestTransaction(requests=requests_list)
242+
# prepare withdrawal senders
243+
withdrawal_request_transaction.update_pre(pre=pre)
244+
# get transaction list
245+
txs: List[Transaction] = withdrawal_request_transaction.transactions()
246+
247+
blockchain_test(
248+
pre=pre,
249+
blocks=[
250+
Block(
251+
txs=txs,
252+
requests_hash=Requests(*requests_list),
253+
),
254+
],
255+
post={},
256+
)

0 commit comments

Comments
 (0)