39
39
REFERENCE_SPEC_GIT_PATH = "TODO"
40
40
REFERENCE_SPEC_VERSION = "TODO"
41
41
42
- MAX_CODE_SIZE = 24 * 1024
43
42
KECCAK_RATE = 136
44
43
45
44
@@ -342,6 +341,8 @@ def test_worst_keccak(
342
341
gsc = fork .gas_costs ()
343
342
mem_exp_gas_calculator = fork .memory_expansion_gas_calculator ()
344
343
344
+ max_code_size = fork .max_code_size ()
345
+
345
346
# Discover the optimal input size to maximize keccak-permutations, not keccak calls.
346
347
# The complication of the discovery arises from the non-linear gas cost of memory expansion.
347
348
max_keccak_perm_per_block = 0
@@ -372,18 +373,18 @@ def test_worst_keccak(
372
373
# The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
373
374
#
374
375
# Now calculate available gas for [attack iteration]:
375
- # Numerator = MAX_CODE_SIZE -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
376
+ # Numerator = max_code_size -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
376
377
# Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
377
378
# TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
378
379
# attack, whenever this is fixed adjust accordingly.
379
380
start_code = Op .JUMPDEST + Op .PUSH20 [optimal_input_length ]
380
381
loop_code = Op .POP (Op .SHA3 (Op .PUSH0 , Op .DUP1 ))
381
382
end_code = Op .POP + Op .JUMP (Op .PUSH0 )
382
- max_iters_loop = (MAX_CODE_SIZE - (len (start_code ) + len (end_code ))) // len (loop_code )
383
+ max_iters_loop = (max_code_size - (len (start_code ) + len (end_code ))) // len (loop_code )
383
384
code = start_code + (loop_code * max_iters_loop ) + end_code
384
- if len (code ) > MAX_CODE_SIZE :
385
+ if len (code ) > max_code_size :
385
386
# Must never happen, but keep it as a sanity check.
386
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
387
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
387
388
388
389
code_address = pre .deploy_contract (code = bytes (code ))
389
390
@@ -730,37 +731,37 @@ def test_worst_precompile_fixed_cost(
730
731
)
731
732
732
733
733
- def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode ):
734
+ def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode , fork : Fork ):
734
735
"""Create a code loop that calls a precompile with the given calldata."""
736
+ max_code_size = fork .max_code_size ()
737
+
735
738
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
736
739
jumpdest = Op .JUMPDEST
737
740
jump_back = Op .JUMP (len (calldata ))
738
- max_iters_loop = (MAX_CODE_SIZE - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
741
+ max_iters_loop = (max_code_size - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
739
742
attack_block
740
743
)
741
744
code = calldata + jumpdest + sum ([attack_block ] * max_iters_loop ) + jump_back
742
- if len (code ) > MAX_CODE_SIZE :
745
+ if len (code ) > max_code_size :
743
746
# Must never happen, but keep it as a sanity check.
744
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
747
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
745
748
746
749
return code
747
750
748
751
749
752
@pytest .mark .zkevm
750
753
@pytest .mark .valid_from ("Cancun" )
751
754
@pytest .mark .slow
752
- def test_worst_jumps (
753
- state_test : StateTestFiller ,
754
- pre : Alloc ,
755
- ):
755
+ def test_worst_jumps (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
756
756
"""Test running a JUMP-intensive contract."""
757
757
env = Environment ()
758
+ max_code_size = fork .max_code_size ()
758
759
759
760
def jump_seq ():
760
761
return Op .JUMP (Op .ADD (Op .PC , 1 )) + Op .JUMPDEST
761
762
762
763
bytes_per_seq = len (jump_seq ())
763
- seqs_per_call = MAX_CODE_SIZE // bytes_per_seq
764
+ seqs_per_call = max_code_size // bytes_per_seq
764
765
765
766
# Create and deploy the jump-intensive contract
766
767
jumps_code = sum ([jump_seq () for _ in range (seqs_per_call )])
@@ -787,15 +788,13 @@ def jump_seq():
787
788
@pytest .mark .zkevm
788
789
@pytest .mark .valid_from ("Cancun" )
789
790
@pytest .mark .slow
790
- def test_worst_jumpdests (
791
- state_test : StateTestFiller ,
792
- pre : Alloc ,
793
- ):
791
+ def test_worst_jumpdests (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
794
792
"""Test running a JUMPDEST-intensive contract."""
795
793
env = Environment ()
794
+ max_code_size = fork .max_code_size ()
796
795
797
796
# Create and deploy a contract with many JUMPDESTs
798
- jumpdests_code = sum ([Op .JUMPDEST ] * MAX_CODE_SIZE )
797
+ jumpdests_code = sum ([Op .JUMPDEST ] * max_code_size )
799
798
jumpdests_address = pre .deploy_contract (code = bytes (jumpdests_code ))
800
799
801
800
# Call the contract repeatedly until gas runs out.
@@ -940,26 +939,24 @@ def test_worst_jumpdests(
940
939
ids = lambda param : "" if isinstance (param , tuple ) else param ,
941
940
)
942
941
def test_worst_binop_simple (
943
- state_test : StateTestFiller ,
944
- pre : Alloc ,
945
- opcode : Op ,
946
- opcode_args : tuple [int , int ],
942
+ state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork , opcode_args : tuple [int , int ]
947
943
):
948
944
"""
949
945
Test running a block with as many binary instructions (takes two args, produces one value)
950
946
as possible. The execution starts with two initial values on the stack, and the stack is
951
947
balanced by the DUP2 instruction.
952
948
"""
953
949
env = Environment ()
950
+ max_code_size = fork .max_code_size ()
954
951
955
952
tx_data = b"" .join (arg .to_bytes (32 , byteorder = "big" ) for arg in opcode_args )
956
953
957
954
code_prefix = Op .JUMPDEST + Op .CALLDATALOAD (0 ) + Op .CALLDATALOAD (32 )
958
955
code_suffix = Op .POP + Op .POP + Op .PUSH0 + Op .JUMP
959
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
956
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
960
957
code_body = (Op .DUP2 + opcode ) * (code_body_len // 2 )
961
958
code = code_prefix + code_body + code_suffix
962
- assert len (code ) == MAX_CODE_SIZE - 1
959
+ assert len (code ) == max_code_size - 1
963
960
964
961
tx = Transaction (
965
962
to = pre .deploy_contract (code = code ),
@@ -978,23 +975,20 @@ def test_worst_binop_simple(
978
975
979
976
@pytest .mark .valid_from ("Cancun" )
980
977
@pytest .mark .parametrize ("opcode" , [Op .ISZERO , Op .NOT ])
981
- def test_worst_unop (
982
- state_test : StateTestFiller ,
983
- pre : Alloc ,
984
- opcode : Op ,
985
- ):
978
+ def test_worst_unop (state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork ):
986
979
"""
987
980
Test running a block with as many unary instructions (takes one arg, produces one value)
988
981
as possible.
989
982
"""
990
983
env = Environment ()
984
+ max_code_size = fork .max_code_size ()
991
985
992
986
code_prefix = Op .JUMPDEST + Op .PUSH0 # Start with the arg 0.
993
987
code_suffix = Op .POP + Op .PUSH0 + Op .JUMP
994
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
988
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
995
989
code_body = opcode * code_body_len
996
990
code = code_prefix + code_body + code_suffix
997
- assert len (code ) == MAX_CODE_SIZE
991
+ assert len (code ) == max_code_size
998
992
999
993
tx = Transaction (
1000
994
to = pre .deploy_contract (code = code ),
@@ -1015,13 +1009,15 @@ def test_worst_unop(
1015
1009
def test_worst_shifts (
1016
1010
state_test : StateTestFiller ,
1017
1011
pre : Alloc ,
1012
+ fork : Fork ,
1018
1013
shift_right : Op ,
1019
1014
):
1020
1015
"""
1021
1016
Test running a block with as many shift instructions with non-trivial arguments.
1022
1017
This test generates left-right pairs of shifts to avoid zeroing the argument.
1023
1018
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
1024
1019
"""
1020
+ max_code_size = fork .max_code_size ()
1025
1021
1026
1022
def to_signed (x ):
1027
1023
return x if x < 2 ** 255 else x - 2 ** 256
@@ -1055,7 +1051,7 @@ def sar(x, s):
1055
1051
1056
1052
code_prefix = sum (Op .PUSH1 [sh ] for sh in shift_amounts ) + Op .JUMPDEST + Op .CALLDATALOAD (0 )
1057
1053
code_suffix = Op .POP + Op .JUMP (len (shift_amounts ) * 2 )
1058
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1054
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1059
1055
1060
1056
def select_shift_amount (shift_fn , v ):
1061
1057
"""Select a shift amount that will produce a non-zero result."""
@@ -1075,7 +1071,7 @@ def select_shift_amount(shift_fn, v):
1075
1071
code_body += make_dup (len (shift_amounts ) - i ) + shift_right
1076
1072
1077
1073
code = code_prefix + code_body + code_suffix
1078
- assert len (code ) == MAX_CODE_SIZE - 2
1074
+ assert len (code ) == max_code_size - 2
1079
1075
1080
1076
env = Environment ()
1081
1077
@@ -1113,14 +1109,15 @@ def test_worst_blobhash(
1113
1109
):
1114
1110
"""Test running a block with as many BLOBHASH instructions as possible."""
1115
1111
env = Environment ()
1112
+ max_code_size = fork .max_code_size ()
1116
1113
1117
1114
code_prefix = Op .PUSH1 (blob_index ) + Op .JUMPDEST
1118
1115
code_suffix = Op .JUMP (len (code_prefix ) - 1 )
1119
1116
loop_iter = Op .POP (Op .BLOBHASH (Op .DUP1 ))
1120
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1117
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1121
1118
code_body = loop_iter * code_body_len
1122
1119
code = code_prefix + code_body + code_suffix
1123
- assert len (code ) <= MAX_CODE_SIZE
1120
+ assert len (code ) <= max_code_size
1124
1121
1125
1122
tx_type = TransactionType .LEGACY
1126
1123
blob_versioned_hashes = None
@@ -1156,6 +1153,7 @@ def test_worst_blobhash(
1156
1153
def test_worst_mod (
1157
1154
state_test : StateTestFiller ,
1158
1155
pre : Alloc ,
1156
+ fork : Fork ,
1159
1157
mod_bits : int ,
1160
1158
op : Op ,
1161
1159
):
@@ -1169,6 +1167,8 @@ def test_worst_mod(
1169
1167
The order of accessing the numerators is selected in a way the mod value remains in the range
1170
1168
as long as possible.
1171
1169
"""
1170
+ max_code_size = fork .max_code_size ()
1171
+
1172
1172
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
1173
1173
# just the SMOD implementation will have to additionally handle the sign bits.
1174
1174
# The result stays negative.
@@ -1242,7 +1242,7 @@ def test_worst_mod(
1242
1242
code_constant_pool = sum ((Op .PUSH32 [n ] for n in numerators ), Bytecode ())
1243
1243
code_prefix = code_constant_pool + Op .JUMPDEST
1244
1244
code_suffix = Op .JUMP (len (code_constant_pool ))
1245
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1245
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1246
1246
code_segment = (
1247
1247
Op .CALLDATALOAD (0 ) + sum (make_dup (len (numerators ) - i ) + op for i in indexes ) + Op .POP
1248
1248
)
@@ -1252,7 +1252,7 @@ def test_worst_mod(
1252
1252
+ sum (code_segment for _ in range (code_body_len // len (code_segment )))
1253
1253
+ code_suffix
1254
1254
)
1255
- assert (MAX_CODE_SIZE - len (code_segment )) < len (code ) <= MAX_CODE_SIZE
1255
+ assert (max_code_size - len (code_segment )) < len (code ) <= max_code_size
1256
1256
1257
1257
env = Environment ()
1258
1258
@@ -1280,13 +1280,15 @@ def test_worst_mod(
1280
1280
def test_worst_memory_access (
1281
1281
state_test : StateTestFiller ,
1282
1282
pre : Alloc ,
1283
+ fork : Fork ,
1283
1284
opcode : Op ,
1284
1285
offset : int ,
1285
1286
offset_initialized : bool ,
1286
1287
big_memory_expansion : bool ,
1287
1288
):
1288
1289
"""Test running a block with as many memory access instructions as possible."""
1289
1290
env = Environment ()
1291
+ max_code_size = fork .max_code_size ()
1290
1292
1291
1293
mem_exp_code = Op .MSTORE8 (10 * 1024 , 1 ) if big_memory_expansion else Bytecode ()
1292
1294
offset_set_code = Op .MSTORE (offset , 43 ) if offset_initialized else Bytecode ()
@@ -1296,10 +1298,10 @@ def test_worst_memory_access(
1296
1298
1297
1299
loop_iter = Op .POP (Op .MLOAD (Op .DUP1 )) if opcode == Op .MLOAD else opcode (Op .DUP2 , Op .DUP2 )
1298
1300
1299
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1301
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1300
1302
code_body = loop_iter * code_body_len
1301
1303
code = code_prefix + code_body + code_suffix
1302
- assert len (code ) <= MAX_CODE_SIZE
1304
+ assert len (code ) <= max_code_size
1303
1305
1304
1306
tx = Transaction (
1305
1307
to = pre .deploy_contract (code = code ),
@@ -1446,14 +1448,15 @@ def test_worst_calldataload(
1446
1448
):
1447
1449
"""Test running a block with as many CALLDATALOAD as possible."""
1448
1450
env = Environment ()
1451
+ max_code_size = fork .max_code_size ()
1449
1452
1450
1453
code_prefix = Op .PUSH0 + Op .JUMPDEST
1451
1454
code_suffix = Op .PUSH1 (1 ) + Op .JUMP
1452
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1455
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1453
1456
code_loop_iter = Op .CALLDATALOAD
1454
1457
code_body = code_loop_iter * (code_body_len // len (code_loop_iter ))
1455
1458
code = code_prefix + code_body + code_suffix
1456
- assert len (code ) <= MAX_CODE_SIZE
1459
+ assert len (code ) <= max_code_size
1457
1460
1458
1461
tx = Transaction (
1459
1462
to = pre .deploy_contract (code = code ),
0 commit comments