38
38
REFERENCE_SPEC_GIT_PATH = "TODO"
39
39
REFERENCE_SPEC_VERSION = "TODO"
40
40
41
- MAX_CODE_SIZE = 24 * 1024
42
41
KECCAK_RATE = 136
43
42
44
43
@@ -73,6 +72,8 @@ def test_worst_keccak(
73
72
gsc = fork .gas_costs ()
74
73
mem_exp_gas_calculator = fork .memory_expansion_gas_calculator ()
75
74
75
+ max_code_size = fork .max_code_size ()
76
+
76
77
# Discover the optimal input size to maximize keccak-permutations, not keccak calls.
77
78
# The complication of the discovery arises from the non-linear gas cost of memory expansion.
78
79
max_keccak_perm_per_block = 0
@@ -103,18 +104,18 @@ def test_worst_keccak(
103
104
# The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
104
105
#
105
106
# Now calculate available gas for [attack iteration]:
106
- # Numerator = MAX_CODE_SIZE -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
107
+ # Numerator = max_code_size -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
107
108
# Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
108
109
# TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
109
110
# attack, whenever this is fixed adjust accordingly.
110
111
start_code = Op .JUMPDEST + Op .PUSH20 [optimal_input_length ]
111
112
loop_code = Op .POP (Op .SHA3 (Op .PUSH0 , Op .DUP1 ))
112
113
end_code = Op .POP + Op .JUMP (Op .PUSH0 )
113
- max_iters_loop = (MAX_CODE_SIZE - (len (start_code ) + len (end_code ))) // len (loop_code )
114
+ max_iters_loop = (max_code_size - (len (start_code ) + len (end_code ))) // len (loop_code )
114
115
code = start_code + (loop_code * max_iters_loop ) + end_code
115
- if len (code ) > MAX_CODE_SIZE :
116
+ if len (code ) > max_code_size :
116
117
# Must never happen, but keep it as a sanity check.
117
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
118
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
118
119
119
120
code_address = pre .deploy_contract (code = bytes (code ))
120
121
@@ -461,37 +462,37 @@ def test_worst_precompile_fixed_cost(
461
462
)
462
463
463
464
464
- def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode ):
465
+ def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode , fork : Fork ):
465
466
"""Create a code loop that calls a precompile with the given calldata."""
467
+ max_code_size = fork .max_code_size ()
468
+
466
469
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
467
470
jumpdest = Op .JUMPDEST
468
471
jump_back = Op .JUMP (len (calldata ))
469
- max_iters_loop = (MAX_CODE_SIZE - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
472
+ max_iters_loop = (max_code_size - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
470
473
attack_block
471
474
)
472
475
code = calldata + jumpdest + sum ([attack_block ] * max_iters_loop ) + jump_back
473
- if len (code ) > MAX_CODE_SIZE :
476
+ if len (code ) > max_code_size :
474
477
# Must never happen, but keep it as a sanity check.
475
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
478
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
476
479
477
480
return code
478
481
479
482
480
483
@pytest .mark .zkevm
481
484
@pytest .mark .valid_from ("Cancun" )
482
485
@pytest .mark .slow
483
- def test_worst_jumps (
484
- state_test : StateTestFiller ,
485
- pre : Alloc ,
486
- ):
486
+ def test_worst_jumps (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
487
487
"""Test running a JUMP-intensive contract."""
488
488
env = Environment ()
489
+ max_code_size = fork .max_code_size ()
489
490
490
491
def jump_seq ():
491
492
return Op .JUMP (Op .ADD (Op .PC , 1 )) + Op .JUMPDEST
492
493
493
494
bytes_per_seq = len (jump_seq ())
494
- seqs_per_call = MAX_CODE_SIZE // bytes_per_seq
495
+ seqs_per_call = max_code_size // bytes_per_seq
495
496
496
497
# Create and deploy the jump-intensive contract
497
498
jumps_code = sum ([jump_seq () for _ in range (seqs_per_call )])
@@ -518,15 +519,13 @@ def jump_seq():
518
519
@pytest .mark .zkevm
519
520
@pytest .mark .valid_from ("Cancun" )
520
521
@pytest .mark .slow
521
- def test_worst_jumpdests (
522
- state_test : StateTestFiller ,
523
- pre : Alloc ,
524
- ):
522
+ def test_worst_jumpdests (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
525
523
"""Test running a JUMPDEST-intensive contract."""
526
524
env = Environment ()
525
+ max_code_size = fork .max_code_size ()
527
526
528
527
# Create and deploy a contract with many JUMPDESTs
529
- jumpdests_code = sum ([Op .JUMPDEST ] * MAX_CODE_SIZE )
528
+ jumpdests_code = sum ([Op .JUMPDEST ] * max_code_size )
530
529
jumpdests_address = pre .deploy_contract (code = bytes (jumpdests_code ))
531
530
532
531
# Call the contract repeatedly until gas runs out.
@@ -671,26 +670,24 @@ def test_worst_jumpdests(
671
670
ids = lambda param : "" if isinstance (param , tuple ) else param ,
672
671
)
673
672
def test_worst_binop_simple (
674
- state_test : StateTestFiller ,
675
- pre : Alloc ,
676
- opcode : Op ,
677
- opcode_args : tuple [int , int ],
673
+ state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork , opcode_args : tuple [int , int ]
678
674
):
679
675
"""
680
676
Test running a block with as many binary instructions (takes two args, produces one value)
681
677
as possible. The execution starts with two initial values on the stack, and the stack is
682
678
balanced by the DUP2 instruction.
683
679
"""
684
680
env = Environment ()
681
+ max_code_size = fork .max_code_size ()
685
682
686
683
tx_data = b"" .join (arg .to_bytes (32 , byteorder = "big" ) for arg in opcode_args )
687
684
688
685
code_prefix = Op .JUMPDEST + Op .CALLDATALOAD (0 ) + Op .CALLDATALOAD (32 )
689
686
code_suffix = Op .POP + Op .POP + Op .PUSH0 + Op .JUMP
690
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
687
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
691
688
code_body = (Op .DUP2 + opcode ) * (code_body_len // 2 )
692
689
code = code_prefix + code_body + code_suffix
693
- assert len (code ) == MAX_CODE_SIZE - 1
690
+ assert len (code ) == max_code_size - 1
694
691
695
692
tx = Transaction (
696
693
to = pre .deploy_contract (code = code ),
@@ -709,23 +706,20 @@ def test_worst_binop_simple(
709
706
710
707
@pytest .mark .valid_from ("Cancun" )
711
708
@pytest .mark .parametrize ("opcode" , [Op .ISZERO , Op .NOT ])
712
- def test_worst_unop (
713
- state_test : StateTestFiller ,
714
- pre : Alloc ,
715
- opcode : Op ,
716
- ):
709
+ def test_worst_unop (state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork ):
717
710
"""
718
711
Test running a block with as many unary instructions (takes one arg, produces one value)
719
712
as possible.
720
713
"""
721
714
env = Environment ()
715
+ max_code_size = fork .max_code_size ()
722
716
723
717
code_prefix = Op .JUMPDEST + Op .PUSH0 # Start with the arg 0.
724
718
code_suffix = Op .POP + Op .PUSH0 + Op .JUMP
725
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
719
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
726
720
code_body = opcode * code_body_len
727
721
code = code_prefix + code_body + code_suffix
728
- assert len (code ) == MAX_CODE_SIZE
722
+ assert len (code ) == max_code_size
729
723
730
724
tx = Transaction (
731
725
to = pre .deploy_contract (code = code ),
@@ -746,13 +740,15 @@ def test_worst_unop(
746
740
def test_worst_shifts (
747
741
state_test : StateTestFiller ,
748
742
pre : Alloc ,
743
+ fork : Fork ,
749
744
shift_right : Op ,
750
745
):
751
746
"""
752
747
Test running a block with as many shift instructions with non-trivial arguments.
753
748
This test generates left-right pairs of shifts to avoid zeroing the argument.
754
749
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
755
750
"""
751
+ max_code_size = fork .max_code_size ()
756
752
757
753
def to_signed (x ):
758
754
return x if x < 2 ** 255 else x - 2 ** 256
@@ -786,7 +782,7 @@ def sar(x, s):
786
782
787
783
code_prefix = sum (Op .PUSH1 [sh ] for sh in shift_amounts ) + Op .JUMPDEST + Op .CALLDATALOAD (0 )
788
784
code_suffix = Op .POP + Op .JUMP (len (shift_amounts ) * 2 )
789
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
785
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
790
786
791
787
def select_shift_amount (shift_fn , v ):
792
788
"""Select a shift amount that will produce a non-zero result."""
@@ -806,7 +802,7 @@ def select_shift_amount(shift_fn, v):
806
802
code_body += make_dup (len (shift_amounts ) - i ) + shift_right
807
803
808
804
code = code_prefix + code_body + code_suffix
809
- assert len (code ) == MAX_CODE_SIZE - 2
805
+ assert len (code ) == max_code_size - 2
810
806
811
807
env = Environment ()
812
808
@@ -844,14 +840,15 @@ def test_worst_blobhash(
844
840
):
845
841
"""Test running a block with as many BLOBHASH instructions as possible."""
846
842
env = Environment ()
843
+ max_code_size = fork .max_code_size ()
847
844
848
845
code_prefix = Op .PUSH1 (blob_index ) + Op .JUMPDEST
849
846
code_suffix = Op .JUMP (len (code_prefix ) - 1 )
850
847
loop_iter = Op .POP (Op .BLOBHASH (Op .DUP1 ))
851
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
848
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
852
849
code_body = loop_iter * code_body_len
853
850
code = code_prefix + code_body + code_suffix
854
- assert len (code ) <= MAX_CODE_SIZE
851
+ assert len (code ) <= max_code_size
855
852
856
853
tx_type = TransactionType .LEGACY
857
854
blob_versioned_hashes = None
@@ -887,6 +884,7 @@ def test_worst_blobhash(
887
884
def test_worst_mod (
888
885
state_test : StateTestFiller ,
889
886
pre : Alloc ,
887
+ fork : Fork ,
890
888
mod_bits : int ,
891
889
op : Op ,
892
890
):
@@ -900,6 +898,8 @@ def test_worst_mod(
900
898
The order of accessing the numerators is selected in a way the mod value remains in the range
901
899
as long as possible.
902
900
"""
901
+ max_code_size = fork .max_code_size ()
902
+
903
903
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
904
904
# just the SMOD implementation will have to additionally handle the sign bits.
905
905
# The result stays negative.
@@ -973,7 +973,7 @@ def test_worst_mod(
973
973
code_constant_pool = sum ((Op .PUSH32 [n ] for n in numerators ), Bytecode ())
974
974
code_prefix = code_constant_pool + Op .JUMPDEST
975
975
code_suffix = Op .JUMP (len (code_constant_pool ))
976
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
976
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
977
977
code_segment = (
978
978
Op .CALLDATALOAD (0 ) + sum (make_dup (len (numerators ) - i ) + op for i in indexes ) + Op .POP
979
979
)
@@ -983,7 +983,7 @@ def test_worst_mod(
983
983
+ sum (code_segment for _ in range (code_body_len // len (code_segment )))
984
984
+ code_suffix
985
985
)
986
- assert (MAX_CODE_SIZE - len (code_segment )) < len (code ) <= MAX_CODE_SIZE
986
+ assert (max_code_size - len (code_segment )) < len (code ) <= max_code_size
987
987
988
988
env = Environment ()
989
989
@@ -1011,13 +1011,15 @@ def test_worst_mod(
1011
1011
def test_worst_memory_access (
1012
1012
state_test : StateTestFiller ,
1013
1013
pre : Alloc ,
1014
+ fork : Fork ,
1014
1015
opcode : Op ,
1015
1016
offset : int ,
1016
1017
offset_initialized : bool ,
1017
1018
big_memory_expansion : bool ,
1018
1019
):
1019
1020
"""Test running a block with as many memory access instructions as possible."""
1020
1021
env = Environment ()
1022
+ max_code_size = fork .max_code_size ()
1021
1023
1022
1024
mem_exp_code = Op .MSTORE8 (10 * 1024 , 1 ) if big_memory_expansion else Bytecode ()
1023
1025
offset_set_code = Op .MSTORE (offset , 43 ) if offset_initialized else Bytecode ()
@@ -1027,10 +1029,10 @@ def test_worst_memory_access(
1027
1029
1028
1030
loop_iter = Op .POP (Op .MLOAD (Op .DUP1 )) if opcode == Op .MLOAD else opcode (Op .DUP2 , Op .DUP2 )
1029
1031
1030
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1032
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1031
1033
code_body = loop_iter * code_body_len
1032
1034
code = code_prefix + code_body + code_suffix
1033
- assert len (code ) <= MAX_CODE_SIZE
1035
+ assert len (code ) <= max_code_size
1034
1036
1035
1037
tx = Transaction (
1036
1038
to = pre .deploy_contract (code = code ),
@@ -1177,14 +1179,15 @@ def test_worst_calldataload(
1177
1179
):
1178
1180
"""Test running a block with as many CALLDATALOAD as possible."""
1179
1181
env = Environment ()
1182
+ max_code_size = fork .max_code_size ()
1180
1183
1181
1184
code_prefix = Op .PUSH0 + Op .JUMPDEST
1182
1185
code_suffix = Op .PUSH1 (1 ) + Op .JUMP
1183
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1186
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1184
1187
code_loop_iter = Op .CALLDATALOAD
1185
1188
code_body = code_loop_iter * (code_body_len // len (code_loop_iter ))
1186
1189
code = code_prefix + code_body + code_suffix
1187
- assert len (code ) <= MAX_CODE_SIZE
1190
+ assert len (code ) <= max_code_size
1188
1191
1189
1192
tx = Transaction (
1190
1193
to = pre .deploy_contract (code = code ),
0 commit comments