@@ -504,6 +504,23 @@ def test_worst_selfdestruct_existing(
504
504
) + Op .RETURN (0 , Op .EXTCODESIZE (selfdestructable_contract_addr ))
505
505
initcode_address = pre .deploy_contract (code = initcode )
506
506
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
+ final_storage_gas = (
519
+ gas_costs .G_STORAGE_RESET + gas_costs .G_COLD_SLOAD + (gas_costs .G_VERY_LOW * 2 )
520
+ )
521
+ base_costs = intrinsic_gas_cost_calc () + (gas_costs .G_VERY_LOW * 4 ) + final_storage_gas
522
+ num_contracts = (attack_gas_limit - base_costs ) // loop_cost
523
+
507
524
# Create a factory that deployes a new SELFDESTRUCT contract instance pre-funded depending on
508
525
# the value_bearing parameter. We use CREATE2 so the caller contract can easily reproduce
509
526
# the addresses in a loop for CALLs.
@@ -526,29 +543,16 @@ def test_worst_selfdestruct_existing(
526
543
+ Op .SSTORE (0 , Op .ADD (Op .SLOAD (0 ), 1 ))
527
544
+ Op .RETURN (0 , 32 )
528
545
)
529
- factory_address = pre .deploy_contract (code = factory_code , balance = 10 ** 18 )
546
+
547
+ required_balance = num_contracts if value_bearing else 0 # 1 wei per contract
548
+ factory_address = pre .deploy_contract (code = factory_code , balance = required_balance )
530
549
531
550
factory_caller_code = Op .CALLDATALOAD (0 ) + While (
532
551
body = Op .POP (Op .CALL (address = factory_address )),
533
552
condition = Op .PUSH1 (1 ) + Op .SWAP1 + Op .SUB + Op .DUP1 + Op .ISZERO + Op .ISZERO ,
534
553
)
535
554
factory_caller_address = pre .deploy_contract (code = factory_caller_code )
536
555
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
-
552
556
contracts_deployment_tx = Transaction (
553
557
to = factory_caller_address ,
554
558
gas_limit = env .gas_limit ,
@@ -568,9 +572,9 @@ def test_worst_selfdestruct_existing(
568
572
+ While (
569
573
body = Op .POP (Op .CALL (address = Op .SHA3 (32 - 20 - 1 , 85 )))
570
574
+ 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 ),
575
+ # Only loop if we have enough gas to cover another iteration plus the
576
+ # final storage gas .
577
+ condition = Op .GT (Op .GAS , final_storage_gas + loop_cost ),
574
578
)
575
579
+ Op .SSTORE (0 , 42 ) # Done for successful tx execution assertion below.
576
580
)
@@ -581,12 +585,12 @@ def test_worst_selfdestruct_existing(
581
585
opcode_tx = Transaction (
582
586
to = code_addr ,
583
587
gas_limit = attack_gas_limit ,
584
- gas_price = 10 ,
585
588
sender = pre .fund_eoa (),
586
589
)
587
590
588
591
post = {
589
- code_addr : Account (storage = {0 : 42 }) # Check for successful execution.
592
+ factory_address : Account (storage = {0 : num_contracts }),
593
+ code_addr : Account (storage = {0 : 42 }), # Check for successful execution.
590
594
}
591
595
deployed_contract_addresses = []
592
596
for i in range (num_contracts ):
@@ -616,6 +620,7 @@ def test_worst_selfdestruct_created(
616
620
state_test : StateTestFiller ,
617
621
pre : Alloc ,
618
622
value_bearing : bool ,
623
+ fork : Fork ,
619
624
):
620
625
"""
621
626
Test running a block with as many SELFDESTRUCTs as possible for deployed contracts in
@@ -625,7 +630,11 @@ def test_worst_selfdestruct_created(
625
630
pre .fund_address (env .fee_recipient , 1 )
626
631
627
632
# SELFDESTRUCT(COINBASE) contract deployment
628
- initcode = Op .MSTORE8 (0 , 0x41 ) + Op .MSTORE8 (1 , 0xFF ) + Op .RETURN (0 , 2 )
633
+ initcode = (
634
+ Op .MSTORE8 (0 , Op .COINBASE .int ()) + Op .MSTORE8 (1 , Op .SELFDESTRUCT .int ()) + Op .RETURN (0 , 2 )
635
+ )
636
+ gas_costs = fork .gas_costs ()
637
+ create_gas = gas_costs .G_CREATE + 20_000
629
638
code = (
630
639
Op .MSTORE (0 , initcode .hex ())
631
640
+ While (
@@ -640,7 +649,7 @@ def test_worst_selfdestruct_created(
640
649
),
641
650
# Stop before we run out of gas for the whole tx execution.
642
651
# The value was found by trial-error rounded to the next 1000 multiple.
643
- condition = Op .GT (Op .GAS , 10_000 ),
652
+ condition = Op .GT (Op .GAS , create_gas ),
644
653
)
645
654
+ Op .SSTORE (0 , 42 ) # Done for successful tx execution assertion below.
646
655
)
@@ -668,12 +677,15 @@ def test_worst_selfdestruct_initcode(
668
677
state_test : StateTestFiller ,
669
678
pre : Alloc ,
670
679
value_bearing : bool ,
680
+ fork : Fork ,
671
681
):
672
682
"""Test running a block with as many SELFDESTRUCTs as possible executed in initcode."""
673
683
env = Environment ()
674
684
pre .fund_address (env .fee_recipient , 1 )
675
685
676
686
initcode = Op .SELFDESTRUCT (Op .COINBASE )
687
+ gas_costs = fork .gas_costs ()
688
+ create_gas = gas_costs .G_CREATE + 20_000
677
689
code = (
678
690
Op .MSTORE (0 , initcode .hex ())
679
691
+ While (
@@ -686,7 +698,7 @@ def test_worst_selfdestruct_initcode(
686
698
),
687
699
# Stop before we run out of gas for the whole tx execution.
688
700
# The value was found by trial-error rounded to the next 1000 multiple.
689
- condition = Op .GT (Op .GAS , 12_000 ),
701
+ condition = Op .GT (Op .GAS , create_gas ),
690
702
)
691
703
+ Op .SSTORE (0 , 42 ) # Done for successful tx execution assertion below.
692
704
)
0 commit comments