2222 Fork ,
2323 JumpLoopGenerator ,
2424 Op ,
25- Transaction ,
2625)
2726
2827from tests .benchmark .compute .helpers import (
29- CallDataOrigin ,
3028 ReturnDataStyle ,
3129)
3230
@@ -64,44 +62,50 @@ def test_calldatasize(
6462 )
6563
6664
67- @pytest .mark .repricing (non_zero_value = True , from_origin = True )
6865@pytest .mark .parametrize ("non_zero_value" , [True , False ])
69- @pytest .mark .parametrize ("from_origin" , [True , False ])
70- def test_callvalue (
66+ def test_callvalue_from_origin (
7167 benchmark_test : BenchmarkTestFiller ,
72- pre : Alloc ,
73- fork : Fork ,
7468 non_zero_value : bool ,
75- from_origin : bool ,
7669) -> None :
7770 """
78- Benchmark CALLVALUE instruction.
79-
80- - non_zero_value: whether opcode must return non-zero value.
81- - from_origin: whether the call frame is the immediate one
82- from the transaction or a previous CALL.
71+ Benchmark CALLVALUE instruction from origin.
8372 """
84- code_address = JumpLoopGenerator (
85- attack_block = Op .POP (Op .CALLVALUE )
86- ).deploy_contracts (pre = pre , fork = fork )
73+ benchmark_test (
74+ code_generator = JumpLoopGenerator (
75+ attack_block = Op .POP (Op .CALLVALUE ),
76+ tx_kwargs = {"value" : int (non_zero_value )},
77+ ),
78+ )
8779
88- if from_origin :
89- tx_to = code_address
90- else :
91- entry_code = (
92- Op .JUMPDEST
93- + Op .CALL (address = code_address , value = 1 if non_zero_value else 0 )
94- + Op .JUMP (Op .PUSH0 )
95- )
96- tx_to = pre .deploy_contract (code = entry_code , balance = 1_000_000 )
9780
98- tx = Transaction (
99- to = tx_to ,
100- value = 1 if non_zero_value and from_origin else 0 ,
101- sender = pre .fund_eoa (),
81+ @pytest .mark .parametrize ("non_zero_value" , [True , False ])
82+ def test_callvalue_from_call (
83+ benchmark_test : BenchmarkTestFiller ,
84+ pre : Alloc ,
85+ non_zero_value : bool ,
86+ fork : Fork ,
87+ ) -> None :
88+ """
89+ Benchmark CALLVALUE instruction from call.
90+ """
91+ code_address = pre .deploy_contract (
92+ code = Op .CALLVALUE * fork .max_stack_height ()
93+ )
94+ benchmark_test (
95+ code_generator = JumpLoopGenerator (
96+ attack_block = Op .POP (
97+ Op .CALL (
98+ address = code_address ,
99+ value = int (non_zero_value ),
100+ args_offset = Op .PUSH0 ,
101+ args_size = Op .PUSH0 ,
102+ ret_offset = Op .PUSH0 ,
103+ ret_size = Op .PUSH0 ,
104+ )
105+ ),
106+ tx_kwargs = {"value" : 10 ** 18 },
107+ ),
102108 )
103-
104- benchmark_test (tx = tx )
105109
106110
107111@pytest .mark .repricing (calldata = b"" )
@@ -128,12 +132,77 @@ def test_calldataload(
128132
129133
130134@pytest .mark .parametrize (
131- "origin" ,
135+ "size" ,
136+ [
137+ pytest .param (0 , id = "0 bytes" ),
138+ pytest .param (100 , id = "100 bytes" ),
139+ pytest .param (10 * 1024 , id = "10KiB" ),
140+ pytest .param (1024 * 1024 , id = "1MiB" ),
141+ ],
142+ )
143+ @pytest .mark .parametrize (
144+ "fixed_src_dst" ,
132145 [
133- pytest . param ( CallDataOrigin . TRANSACTION , id = "transaction" ) ,
134- pytest . param ( CallDataOrigin . CALL , id = "call" ) ,
146+ True ,
147+ False ,
135148 ],
136149)
150+ @pytest .mark .parametrize (
151+ "non_zero_data" ,
152+ [
153+ True ,
154+ False ,
155+ ],
156+ )
157+ def test_calldatacopy_from_origin (
158+ benchmark_test : BenchmarkTestFiller ,
159+ fork : Fork ,
160+ size : int ,
161+ fixed_src_dst : bool ,
162+ non_zero_data : bool ,
163+ tx_gas_limit : int ,
164+ ) -> None :
165+ """Benchmark CALLDATACOPY instruction."""
166+ if size == 0 and non_zero_data :
167+ pytest .skip ("Non-zero data with size 0 is not applicable." )
168+
169+ # If `non_zero_data` is True, we fill the calldata with deterministic
170+ # random data. Note that if `size == 0` and `non_zero_data` is a skipped
171+ # case.
172+ data = Bytes ([i % 256 for i in range (size )]) if non_zero_data else Bytes ()
173+
174+ intrinsic_gas_calculator = fork .transaction_intrinsic_cost_calculator ()
175+ min_gas = intrinsic_gas_calculator (calldata = data )
176+ if min_gas > tx_gas_limit :
177+ pytest .skip (
178+ "Minimum gas required for calldata ({min_gas}) is greater "
179+ "than the gas limit"
180+ )
181+
182+ # We create the contract that will be doing the CALLDATACOPY multiple
183+ # times.
184+ #
185+ # If `non_zero_data` is True, we leverage CALLDATASIZE for the copy
186+ # length. Otherwise, since we
187+ # don't send zero data explicitly via calldata, PUSH the target size and
188+ # use DUP1 to copy it.
189+ setup = Op .CALLDATASIZE if non_zero_data or size == 0 else Op .PUSH3 (size )
190+ src_dst = 0 if fixed_src_dst else Op .AND (Op .GAS , 7 )
191+ attack_block = Op .CALLDATACOPY (
192+ src_dst ,
193+ src_dst ,
194+ Op .DUP1 ,
195+ )
196+
197+ benchmark_test (
198+ code_generator = JumpLoopGenerator (
199+ setup = setup ,
200+ attack_block = attack_block ,
201+ tx_kwargs = {"data" : data },
202+ )
203+ )
204+
205+
137206@pytest .mark .parametrize (
138207 "size" ,
139208 [
@@ -157,11 +226,9 @@ def test_calldataload(
157226 False ,
158227 ],
159228)
160- def test_calldatacopy (
229+ def test_calldatacopy_from_call (
161230 benchmark_test : BenchmarkTestFiller ,
162- pre : Alloc ,
163231 fork : Fork ,
164- origin : CallDataOrigin ,
165232 size : int ,
166233 fixed_src_dst : bool ,
167234 non_zero_data : bool ,
@@ -192,46 +259,21 @@ def test_calldatacopy(
192259 # don't send zero data explicitly via calldata, PUSH the target size and
193260 # use DUP1 to copy it.
194261 setup = Bytecode () if non_zero_data or size == 0 else Op .PUSH3 (size )
195- src_dst = 0 if fixed_src_dst else Op .MOD (Op .GAS , 7 )
262+ src_dst = 0 if fixed_src_dst else Op .AND (Op .GAS , 7 )
196263 attack_block = Op .CALLDATACOPY (
197264 src_dst ,
198265 src_dst ,
199266 Op .CALLDATASIZE if non_zero_data or size == 0 else Op .DUP1 ,
200267 )
201268
202- code_address = JumpLoopGenerator (
203- setup = setup , attack_block = attack_block
204- ).deploy_contracts (pre = pre , fork = fork )
205-
206- tx_target = code_address
207-
208- # If the origin is CALL, we need to create a contract that will call the
209- # target contract with the calldata.
210- if origin == CallDataOrigin .CALL :
211- # If `non_zero_data` is False we leverage just using zeroed memory.
212- # Otherwise, we copy the calldata received from the transaction.
213- setup = (
214- Op .CALLDATACOPY (Op .PUSH0 , Op .PUSH0 , Op .CALLDATASIZE )
215- if non_zero_data
216- else Bytecode ()
217- ) + Op .JUMPDEST
218- arg_size = Op .CALLDATASIZE if non_zero_data else size
219- attack_block = Op .STATICCALL (
220- address = code_address , args_offset = Op .PUSH0 , args_size = arg_size
269+ benchmark_test (
270+ code_generator = ExtCallGenerator (
271+ setup = setup ,
272+ attack_block = attack_block ,
273+ tx_kwargs = {"data" : data },
221274 )
222-
223- tx_target = JumpLoopGenerator (
224- setup = setup , attack_block = attack_block
225- ).deploy_contracts (pre = pre , fork = fork )
226-
227- tx = Transaction (
228- to = tx_target ,
229- data = data ,
230- sender = pre .fund_eoa (),
231275 )
232276
233- benchmark_test (tx = tx )
234-
235277
236278@pytest .mark .repricing (
237279 returned_size = 1 ,
0 commit comments