40
40
REFERENCE_SPEC_GIT_PATH = "TODO"
41
41
REFERENCE_SPEC_VERSION = "TODO"
42
42
43
- MAX_CODE_SIZE = 24 * 1024
44
43
KECCAK_RATE = 136
45
44
46
45
@@ -343,6 +342,8 @@ def test_worst_keccak(
343
342
gsc = fork .gas_costs ()
344
343
mem_exp_gas_calculator = fork .memory_expansion_gas_calculator ()
345
344
345
+ max_code_size = fork .max_code_size ()
346
+
346
347
# Discover the optimal input size to maximize keccak-permutations, not keccak calls.
347
348
# The complication of the discovery arises from the non-linear gas cost of memory expansion.
348
349
max_keccak_perm_per_block = 0
@@ -373,18 +374,18 @@ def test_worst_keccak(
373
374
# The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
374
375
#
375
376
# Now calculate available gas for [attack iteration]:
376
- # Numerator = MAX_CODE_SIZE -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
377
+ # Numerator = max_code_size -3. The -3 is for the JUMPDEST, PUSH0 and JUMP.
377
378
# Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
378
379
# TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
379
380
# attack, whenever this is fixed adjust accordingly.
380
381
start_code = Op .JUMPDEST + Op .PUSH20 [optimal_input_length ]
381
382
loop_code = Op .POP (Op .SHA3 (Op .PUSH0 , Op .DUP1 ))
382
383
end_code = Op .POP + Op .JUMP (Op .PUSH0 )
383
- max_iters_loop = (MAX_CODE_SIZE - (len (start_code ) + len (end_code ))) // len (loop_code )
384
+ max_iters_loop = (max_code_size - (len (start_code ) + len (end_code ))) // len (loop_code )
384
385
code = start_code + (loop_code * max_iters_loop ) + end_code
385
- if len (code ) > MAX_CODE_SIZE :
386
+ if len (code ) > max_code_size :
386
387
# Must never happen, but keep it as a sanity check.
387
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
388
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
388
389
389
390
code_address = pre .deploy_contract (code = bytes (code ))
390
391
@@ -731,37 +732,37 @@ def test_worst_precompile_fixed_cost(
731
732
)
732
733
733
734
734
- def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode ):
735
+ def code_loop_precompile_call (calldata : Bytecode , attack_block : Bytecode , fork : Fork ):
735
736
"""Create a code loop that calls a precompile with the given calldata."""
737
+ max_code_size = fork .max_code_size ()
738
+
736
739
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
737
740
jumpdest = Op .JUMPDEST
738
741
jump_back = Op .JUMP (len (calldata ))
739
- max_iters_loop = (MAX_CODE_SIZE - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
742
+ max_iters_loop = (max_code_size - len (calldata ) - len (jumpdest ) - len (jump_back )) // len (
740
743
attack_block
741
744
)
742
745
code = calldata + jumpdest + sum ([attack_block ] * max_iters_loop ) + jump_back
743
- if len (code ) > MAX_CODE_SIZE :
746
+ if len (code ) > max_code_size :
744
747
# Must never happen, but keep it as a sanity check.
745
- raise ValueError (f"Code size { len (code )} exceeds maximum code size { MAX_CODE_SIZE } " )
748
+ raise ValueError (f"Code size { len (code )} exceeds maximum code size { max_code_size } " )
746
749
747
750
return code
748
751
749
752
750
753
@pytest .mark .zkevm
751
754
@pytest .mark .valid_from ("Cancun" )
752
755
@pytest .mark .slow
753
- def test_worst_jumps (
754
- state_test : StateTestFiller ,
755
- pre : Alloc ,
756
- ):
756
+ def test_worst_jumps (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
757
757
"""Test running a JUMP-intensive contract."""
758
758
env = Environment ()
759
+ max_code_size = fork .max_code_size ()
759
760
760
761
def jump_seq ():
761
762
return Op .JUMP (Op .ADD (Op .PC , 1 )) + Op .JUMPDEST
762
763
763
764
bytes_per_seq = len (jump_seq ())
764
- seqs_per_call = MAX_CODE_SIZE // bytes_per_seq
765
+ seqs_per_call = max_code_size // bytes_per_seq
765
766
766
767
# Create and deploy the jump-intensive contract
767
768
jumps_code = sum ([jump_seq () for _ in range (seqs_per_call )])
@@ -788,15 +789,13 @@ def jump_seq():
788
789
@pytest .mark .zkevm
789
790
@pytest .mark .valid_from ("Cancun" )
790
791
@pytest .mark .slow
791
- def test_worst_jumpdests (
792
- state_test : StateTestFiller ,
793
- pre : Alloc ,
794
- ):
792
+ def test_worst_jumpdests (state_test : StateTestFiller , pre : Alloc , fork : Fork ):
795
793
"""Test running a JUMPDEST-intensive contract."""
796
794
env = Environment ()
795
+ max_code_size = fork .max_code_size ()
797
796
798
797
# Create and deploy a contract with many JUMPDESTs
799
- jumpdests_code = sum ([Op .JUMPDEST ] * MAX_CODE_SIZE )
798
+ jumpdests_code = sum ([Op .JUMPDEST ] * max_code_size )
800
799
jumpdests_address = pre .deploy_contract (code = bytes (jumpdests_code ))
801
800
802
801
# Call the contract repeatedly until gas runs out.
@@ -961,26 +960,24 @@ def test_worst_jumpdests(
961
960
ids = lambda param : "" if isinstance (param , tuple ) else param ,
962
961
)
963
962
def test_worst_binop_simple (
964
- state_test : StateTestFiller ,
965
- pre : Alloc ,
966
- opcode : Op ,
967
- opcode_args : tuple [int , int ],
963
+ state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork , opcode_args : tuple [int , int ]
968
964
):
969
965
"""
970
966
Test running a block with as many binary instructions (takes two args, produces one value)
971
967
as possible. The execution starts with two initial values on the stack, and the stack is
972
968
balanced by the DUP2 instruction.
973
969
"""
974
970
env = Environment ()
971
+ max_code_size = fork .max_code_size ()
975
972
976
973
tx_data = b"" .join (arg .to_bytes (32 , byteorder = "big" ) for arg in opcode_args )
977
974
978
975
code_prefix = Op .JUMPDEST + Op .CALLDATALOAD (0 ) + Op .CALLDATALOAD (32 )
979
976
code_suffix = Op .POP + Op .POP + Op .PUSH0 + Op .JUMP
980
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
977
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
981
978
code_body = (Op .DUP2 + opcode ) * (code_body_len // 2 )
982
979
code = code_prefix + code_body + code_suffix
983
- assert len (code ) == MAX_CODE_SIZE - 1
980
+ assert len (code ) == max_code_size - 1
984
981
985
982
tx = Transaction (
986
983
to = pre .deploy_contract (code = code ),
@@ -999,23 +996,20 @@ def test_worst_binop_simple(
999
996
1000
997
@pytest .mark .valid_from ("Cancun" )
1001
998
@pytest .mark .parametrize ("opcode" , [Op .ISZERO , Op .NOT ])
1002
- def test_worst_unop (
1003
- state_test : StateTestFiller ,
1004
- pre : Alloc ,
1005
- opcode : Op ,
1006
- ):
999
+ def test_worst_unop (state_test : StateTestFiller , pre : Alloc , opcode : Op , fork : Fork ):
1007
1000
"""
1008
1001
Test running a block with as many unary instructions (takes one arg, produces one value)
1009
1002
as possible.
1010
1003
"""
1011
1004
env = Environment ()
1005
+ max_code_size = fork .max_code_size ()
1012
1006
1013
1007
code_prefix = Op .JUMPDEST + Op .PUSH0 # Start with the arg 0.
1014
1008
code_suffix = Op .POP + Op .PUSH0 + Op .JUMP
1015
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1009
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1016
1010
code_body = opcode * code_body_len
1017
1011
code = code_prefix + code_body + code_suffix
1018
- assert len (code ) == MAX_CODE_SIZE
1012
+ assert len (code ) == max_code_size
1019
1013
1020
1014
tx = Transaction (
1021
1015
to = pre .deploy_contract (code = code ),
@@ -1036,13 +1030,15 @@ def test_worst_unop(
1036
1030
def test_worst_shifts (
1037
1031
state_test : StateTestFiller ,
1038
1032
pre : Alloc ,
1033
+ fork : Fork ,
1039
1034
shift_right : Op ,
1040
1035
):
1041
1036
"""
1042
1037
Test running a block with as many shift instructions with non-trivial arguments.
1043
1038
This test generates left-right pairs of shifts to avoid zeroing the argument.
1044
1039
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
1045
1040
"""
1041
+ max_code_size = fork .max_code_size ()
1046
1042
1047
1043
def to_signed (x ):
1048
1044
return x if x < 2 ** 255 else x - 2 ** 256
@@ -1076,7 +1072,7 @@ def sar(x, s):
1076
1072
1077
1073
code_prefix = sum (Op .PUSH1 [sh ] for sh in shift_amounts ) + Op .JUMPDEST + Op .CALLDATALOAD (0 )
1078
1074
code_suffix = Op .POP + Op .JUMP (len (shift_amounts ) * 2 )
1079
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1075
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1080
1076
1081
1077
def select_shift_amount (shift_fn , v ):
1082
1078
"""Select a shift amount that will produce a non-zero result."""
@@ -1096,7 +1092,7 @@ def select_shift_amount(shift_fn, v):
1096
1092
code_body += make_dup (len (shift_amounts ) - i ) + shift_right
1097
1093
1098
1094
code = code_prefix + code_body + code_suffix
1099
- assert len (code ) == MAX_CODE_SIZE - 2
1095
+ assert len (code ) == max_code_size - 2
1100
1096
1101
1097
env = Environment ()
1102
1098
@@ -1134,14 +1130,15 @@ def test_worst_blobhash(
1134
1130
):
1135
1131
"""Test running a block with as many BLOBHASH instructions as possible."""
1136
1132
env = Environment ()
1133
+ max_code_size = fork .max_code_size ()
1137
1134
1138
1135
code_prefix = Op .PUSH1 (blob_index ) + Op .JUMPDEST
1139
1136
code_suffix = Op .JUMP (len (code_prefix ) - 1 )
1140
1137
loop_iter = Op .POP (Op .BLOBHASH (Op .DUP1 ))
1141
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1138
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1142
1139
code_body = loop_iter * code_body_len
1143
1140
code = code_prefix + code_body + code_suffix
1144
- assert len (code ) <= MAX_CODE_SIZE
1141
+ assert len (code ) <= max_code_size
1145
1142
1146
1143
tx_type = TransactionType .LEGACY
1147
1144
blob_versioned_hashes = None
@@ -1177,6 +1174,7 @@ def test_worst_blobhash(
1177
1174
def test_worst_mod (
1178
1175
state_test : StateTestFiller ,
1179
1176
pre : Alloc ,
1177
+ fork : Fork ,
1180
1178
mod_bits : int ,
1181
1179
op : Op ,
1182
1180
):
@@ -1190,6 +1188,8 @@ def test_worst_mod(
1190
1188
The order of accessing the numerators is selected in a way the mod value remains in the range
1191
1189
as long as possible.
1192
1190
"""
1191
+ max_code_size = fork .max_code_size ()
1192
+
1193
1193
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
1194
1194
# just the SMOD implementation will have to additionally handle the sign bits.
1195
1195
# The result stays negative.
@@ -1263,7 +1263,7 @@ def test_worst_mod(
1263
1263
code_constant_pool = sum ((Op .PUSH32 [n ] for n in numerators ), Bytecode ())
1264
1264
code_prefix = code_constant_pool + Op .JUMPDEST
1265
1265
code_suffix = Op .JUMP (len (code_constant_pool ))
1266
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1266
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1267
1267
code_segment = (
1268
1268
Op .CALLDATALOAD (0 ) + sum (make_dup (len (numerators ) - i ) + op for i in indexes ) + Op .POP
1269
1269
)
@@ -1273,7 +1273,7 @@ def test_worst_mod(
1273
1273
+ sum (code_segment for _ in range (code_body_len // len (code_segment )))
1274
1274
+ code_suffix
1275
1275
)
1276
- assert (MAX_CODE_SIZE - len (code_segment )) < len (code ) <= MAX_CODE_SIZE
1276
+ assert (max_code_size - len (code_segment )) < len (code ) <= max_code_size
1277
1277
1278
1278
env = Environment ()
1279
1279
@@ -1301,13 +1301,15 @@ def test_worst_mod(
1301
1301
def test_worst_memory_access (
1302
1302
state_test : StateTestFiller ,
1303
1303
pre : Alloc ,
1304
+ fork : Fork ,
1304
1305
opcode : Op ,
1305
1306
offset : int ,
1306
1307
offset_initialized : bool ,
1307
1308
big_memory_expansion : bool ,
1308
1309
):
1309
1310
"""Test running a block with as many memory access instructions as possible."""
1310
1311
env = Environment ()
1312
+ max_code_size = fork .max_code_size ()
1311
1313
1312
1314
mem_exp_code = Op .MSTORE8 (10 * 1024 , 1 ) if big_memory_expansion else Bytecode ()
1313
1315
offset_set_code = Op .MSTORE (offset , 43 ) if offset_initialized else Bytecode ()
@@ -1317,10 +1319,10 @@ def test_worst_memory_access(
1317
1319
1318
1320
loop_iter = Op .POP (Op .MLOAD (Op .DUP1 )) if opcode == Op .MLOAD else opcode (Op .DUP2 , Op .DUP2 )
1319
1321
1320
- code_body_len = (MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1322
+ code_body_len = (max_code_size - len (code_prefix ) - len (code_suffix )) // len (loop_iter )
1321
1323
code_body = loop_iter * code_body_len
1322
1324
code = code_prefix + code_body + code_suffix
1323
- assert len (code ) <= MAX_CODE_SIZE
1325
+ assert len (code ) <= max_code_size
1324
1326
1325
1327
tx = Transaction (
1326
1328
to = pre .deploy_contract (code = code ),
@@ -1570,14 +1572,15 @@ def test_worst_calldataload(
1570
1572
):
1571
1573
"""Test running a block with as many CALLDATALOAD as possible."""
1572
1574
env = Environment ()
1575
+ max_code_size = fork .max_code_size ()
1573
1576
1574
1577
code_prefix = Op .PUSH0 + Op .JUMPDEST
1575
1578
code_suffix = Op .PUSH1 (1 ) + Op .JUMP
1576
- code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
1579
+ code_body_len = max_code_size - len (code_prefix ) - len (code_suffix )
1577
1580
code_loop_iter = Op .CALLDATALOAD
1578
1581
code_body = code_loop_iter * (code_body_len // len (code_loop_iter ))
1579
1582
code = code_prefix + code_body + code_suffix
1580
- assert len (code ) <= MAX_CODE_SIZE
1583
+ assert len (code ) <= max_code_size
1581
1584
1582
1585
tx = Transaction (
1583
1586
to = pre .deploy_contract (code = code ),
0 commit comments