1717 Account ,
1818 Address ,
1919 Alloc ,
20+ AuthorizationTuple ,
2021 Block ,
2122 Bytecode ,
2223 Environment ,
@@ -285,7 +286,7 @@ def test_worst_storage_access_cold(
285286 fork : Fork ,
286287 storage_action : StorageAction ,
287288 absent_slots : bool ,
288- env : Environment ,
289+ tx_gas_limit_cap : int ,
289290 gas_benchmark_value : int ,
290291 tx_result : TransactionResult ,
291292) -> None :
@@ -328,12 +329,13 @@ def test_worst_storage_access_cold(
328329 # Add costs jump-logic costs
329330 loop_cost += (
330331 gas_costs .G_JUMPDEST # Prefix Jumpdest
331- + gas_costs .G_VERY_LOW * 7 # ISZEROs, PUSHs, SWAPs, SUB, DUP
332+ + gas_costs .G_VERY_LOW * 5 # GT, PUSHs, SWAPs, SUB, DUP
333+ + gas_costs .G_MID # SELFBALANCE
332334 + gas_costs .G_HIGH # JUMPI
333335 )
334336
335337 prefix_cost = (
336- gas_costs .G_VERY_LOW # Target slots push
338+ gas_costs .G_VERY_LOW * 2 # CALLDATALOAD(0)
337339 )
338340
339341 suffix_cost = 0
@@ -348,14 +350,15 @@ def test_worst_storage_access_cold(
348350 - prefix_cost
349351 - suffix_cost
350352 ) // loop_cost
353+
351354 if tx_result == TransactionResult .OUT_OF_GAS :
352355 # Add an extra slot to make it run out-of-gas
353356 num_target_slots += 1
354357
355- code_prefix = Op .PUSH4 ( num_target_slots ) + Op .JUMPDEST
358+ code_prefix = Op .CALLDATALOAD ( 0 ) + Op .JUMPDEST
356359 code_loop = execution_code_body + Op .JUMPI (
357360 len (code_prefix ) - 1 ,
358- Op .PUSH1 (1 ) + Op .SWAP1 + Op .SUB + Op . DUP1 + Op .ISZERO + Op .ISZERO ,
361+ Op .PUSH1 (1 ) + Op .ADD + Op .DUP1 + Op .SELFBALANCE + Op .GT ,
359362 )
360363 execution_code = code_prefix + code_loop
361364
@@ -366,57 +369,123 @@ def test_worst_storage_access_cold(
366369
367370 execution_code_address = pre .deploy_contract (code = execution_code )
368371
369- total_gas_used = (
370- num_target_slots * loop_cost
371- + intrinsic_gas_cost_calc ()
372- + prefix_cost
373- + suffix_cost
374- )
372+ blocks = []
373+ target_addr = execution_code_address
375374
376- # Contract creation
377- slots_init = Bytecode ()
375+ # Setup the target address
378376 if not absent_slots :
379- slots_init = Op .PUSH4 (num_target_slots ) + While (
380- body = Op .SSTORE (Op .DUP1 , Op .DUP1 ),
381- condition = Op .PUSH1 (1 )
382- + Op .SWAP1
383- + Op .SUB
384- + Op .DUP1
385- + Op .ISZERO
386- + Op .ISZERO ,
377+ init_prefix = Op .CALLDATALOAD (0 ) + Op .JUMPDEST
378+ init_loop = Op .SSTORE (Op .DUP1 , Op .DUP1 )
379+ init_condition = Op .JUMPI (
380+ len (init_prefix ) - 1 ,
381+ Op .PUSH1 (1 ) + Op .ADD + Op .DUP1 + Op .SELFBALANCE + Op .GT ,
387382 )
383+ init_contract = init_prefix + init_loop + init_condition
388384
389- # To create the contract, we apply the slots_init code to initialize the
390- # storage slots (int the case of absent_slots=False) and then copy the
391- # execution code to the contract.
392- creation_code = (
393- slots_init
394- + Op .EXTCODECOPY (
395- address = execution_code_address ,
396- dest_offset = 0 ,
397- offset = 0 ,
398- size = Op .EXTCODESIZE (execution_code_address ),
385+ target_addr = pre .fund_eoa (
386+ amount = 0 , delegation = pre .deploy_contract (code = init_contract )
399387 )
400- + Op .RETURN (0 , Op .MSIZE )
401- )
402- sender_addr = pre .fund_eoa ()
403- setup_tx = Transaction (
404- to = None ,
405- gas_limit = env .gas_limit ,
406- data = creation_code ,
407- sender = sender_addr ,
408- )
409388
410- blocks = [Block (txs = [setup_tx ])]
389+ init_prefix_overhead = gas_costs .G_VERY_LOW * 2 + gas_costs .G_JUMPDEST
390+ init_loop_overhead = (
391+ gas_costs .G_VERY_LOW * 2 + gas_costs .G_WARM_ACCOUNT_ACCESS
392+ )
393+ init_condition_overhead = (
394+ gas_costs .G_VERY_LOW * 5 + gas_costs .G_MID + gas_costs .G_HIGH
395+ )
396+ init_total_overhead = (
397+ init_prefix_overhead + init_loop_overhead + init_condition_overhead
398+ )
399+ iteration_count = (
400+ gas_benchmark_value - intrinsic_gas_cost_calc ()
401+ ) // init_total_overhead
411402
412- contract_address = compute_create_address (address = sender_addr , nonce = 0 )
403+ tx_count = gas_benchmark_value // tx_gas_limit_cap
404+ total_txs = []
405+ for i in range (tx_count * 2 ):
406+ tx = Transaction (
407+ to = target_addr ,
408+ data = Hash (i * iteration_count ),
409+ value = iteration_count ,
410+ gas_limit = tx_gas_limit_cap ,
411+ sender = pre .fund_eoa (),
412+ )
413+ total_txs .append (tx )
414+ blocks .append (Block (txs = total_txs [:tx_count ]))
415+ blocks .append (Block (txs = total_txs [tx_count :]))
413416
414- op_tx = Transaction (
415- to = contract_address ,
416- gas_limit = gas_benchmark_value ,
417- sender = pre .fund_eoa (),
418- )
419- blocks .append (Block (txs = [op_tx ]))
417+ tx = Transaction (
418+ to = execution_code_address ,
419+ gas_limit = tx_gas_limit_cap ,
420+ sender = pre .fund_eoa (),
421+ authorization_list = [
422+ AuthorizationTuple (
423+ address = execution_code_address ,
424+ nonce = target_addr .nonce ,
425+ signer = target_addr ,
426+ )
427+ ],
428+ )
429+ blocks .append (Block (txs = [tx ]))
430+
431+ attack_txs = []
432+ gas_remaining = gas_benchmark_value
433+ total_iteration = 0
434+ total_gas_used = 0
435+ while gas_remaining > 0 :
436+ gas_available = min (gas_remaining , tx_gas_limit_cap )
437+
438+ # Calculate minimum gas needed for at least one iteration
439+ min_gas_needed = (
440+ intrinsic_gas_cost_calc () + prefix_cost + loop_cost + suffix_cost
441+ )
442+ if gas_available < min_gas_needed :
443+ break
444+
445+ # Calculate iterations accounting for all overhead costs
446+ if tx_result == TransactionResult .OUT_OF_GAS :
447+ # For OUT_OF_GAS, add an extra iteration to run out of gas
448+ iterations = (
449+ (
450+ gas_available
451+ - intrinsic_gas_cost_calc ()
452+ - prefix_cost
453+ - suffix_cost
454+ )
455+ // loop_cost
456+ ) + 1
457+ tx_gas_used = (
458+ gas_available # Transaction will use all available gas
459+ )
460+ else :
461+ # For SUCCESS and REVERT, calculate iterations properly
462+ iterations = (
463+ gas_available
464+ - intrinsic_gas_cost_calc ()
465+ - prefix_cost
466+ - suffix_cost
467+ ) // loop_cost
468+ tx_gas_used = (
469+ intrinsic_gas_cost_calc ()
470+ + prefix_cost
471+ + loop_cost * iterations
472+ + suffix_cost
473+ )
474+
475+ attack_txs .append (
476+ Transaction (
477+ to = execution_code_address ,
478+ data = Hash (total_iteration ),
479+ value = iterations ,
480+ gas_limit = gas_available ,
481+ sender = pre .fund_eoa (),
482+ )
483+ )
484+ gas_remaining -= gas_available
485+ total_iteration += iterations
486+ total_gas_used += tx_gas_used
487+
488+ blocks .append (Block (txs = attack_txs ))
420489
421490 benchmark_test (
422491 blocks = blocks ,
0 commit comments