@@ -504,6 +504,24 @@ def test_worst_selfdestruct_existing(
504504 ) + Op .RETURN (0 , Op .EXTCODESIZE (selfdestructable_contract_addr ))
505505 initcode_address = pre .deploy_contract (code = initcode )
506506
507+ # Calculate the number of contracts that can be deployed with the available gas.
508+ gas_costs = fork .gas_costs ()
509+ intrinsic_gas_cost_calc = fork .transaction_intrinsic_cost_calculator ()
510+ loop_cost = (
511+ gas_costs .G_KECCAK_256 # KECCAK static cost
512+ + math .ceil (85 / 32 ) * gas_costs .G_KECCAK_256_WORD # KECCAK dynamic cost for CREATE2
513+ + gas_costs .G_VERY_LOW * 3 # ~MSTOREs+ADDs
514+ + gas_costs .G_COLD_ACCOUNT_ACCESS # CALL to self-destructing contract
515+ + gas_costs .G_SELF_DESTRUCT
516+ + 63 # ~Gluing opcodes
517+ )
518+ assert loop_cost == 7720
519+ final_storage_gas = (
520+ gas_costs .G_STORAGE_RESET + gas_costs .G_COLD_SLOAD + (gas_costs .G_VERY_LOW * 2 )
521+ )
522+ base_costs = intrinsic_gas_cost_calc () + (gas_costs .G_VERY_LOW * 4 ) + final_storage_gas
523+ num_contracts = (attack_gas_limit - base_costs ) // loop_cost
524+
507525 # Create a factory that deployes a new SELFDESTRUCT contract instance pre-funded depending on
508526 # the value_bearing parameter. We use CREATE2 so the caller contract can easily reproduce
509527 # the addresses in a loop for CALLs.
@@ -526,29 +544,16 @@ def test_worst_selfdestruct_existing(
526544 + Op .SSTORE (0 , Op .ADD (Op .SLOAD (0 ), 1 ))
527545 + Op .RETURN (0 , 32 )
528546 )
529- factory_address = pre .deploy_contract (code = factory_code , balance = 10 ** 18 )
547+
548+ required_balance = num_contracts if value_bearing else 0 # 1 wei per contract
549+ factory_address = pre .deploy_contract (code = factory_code , balance = required_balance )
530550
531551 factory_caller_code = Op .CALLDATALOAD (0 ) + While (
532552 body = Op .POP (Op .CALL (address = factory_address )),
533553 condition = Op .PUSH1 (1 ) + Op .SWAP1 + Op .SUB + Op .DUP1 + Op .ISZERO + Op .ISZERO ,
534554 )
535555 factory_caller_address = pre .deploy_contract (code = factory_caller_code )
536556
537- gas_costs = fork .gas_costs ()
538- intrinsic_gas_cost_calc = fork .transaction_intrinsic_cost_calculator ()
539- loop_cost = (
540- gas_costs .G_KECCAK_256 # KECCAK static cost
541- + math .ceil (85 / 32 ) * gas_costs .G_KECCAK_256_WORD # KECCAK dynamic cost for CREATE2
542- + gas_costs .G_VERY_LOW * 3 # ~MSTOREs+ADDs
543- + gas_costs .G_COLD_ACCOUNT_ACCESS # CALL to self-destructing contract
544- + gas_costs .G_SELF_DESTRUCT
545- + 30 # ~Gluing opcodes
546- )
547- num_contracts = (
548- # Base available gas = GAS_LIMIT - intrinsic - (out of loop MSTOREs)
549- attack_gas_limit - intrinsic_gas_cost_calc () - gas_costs .G_VERY_LOW * 4
550- ) // loop_cost
551-
552557 contracts_deployment_tx = Transaction (
553558 to = factory_caller_address ,
554559 gas_limit = env .gas_limit ,
@@ -568,9 +573,9 @@ def test_worst_selfdestruct_existing(
568573 + While (
569574 body = Op .POP (Op .CALL (address = Op .SHA3 (32 - 20 - 1 , 85 )))
570575 + Op .MSTORE (32 , Op .ADD (Op .MLOAD (32 ), 1 )),
571- # Stop before we run out of gas for the whole tx execution.
572- # The value was found by trial-error rounded to the next 1000 multiple .
573- condition = Op .GT (Op .GAS , 12_000 ),
576+ # Only loop if we have enough gas to cover another iteration plus the
577+ # final storage gas .
578+ condition = Op .GT (Op .GAS , final_storage_gas + loop_cost ),
574579 )
575580 + Op .SSTORE (0 , 42 ) # Done for successful tx execution assertion below.
576581 )
@@ -581,12 +586,12 @@ def test_worst_selfdestruct_existing(
581586 opcode_tx = Transaction (
582587 to = code_addr ,
583588 gas_limit = attack_gas_limit ,
584- gas_price = 10 ,
585589 sender = pre .fund_eoa (),
586590 )
587591
588592 post = {
589- code_addr : Account (storage = {0 : 42 }) # Check for successful execution.
593+ factory_address : Account (storage = {0 : num_contracts }),
594+ code_addr : Account (storage = {0 : 42 }), # Check for successful execution.
590595 }
591596 deployed_contract_addresses = []
592597 for i in range (num_contracts ):
0 commit comments