Skip to content

Commit 8a40f0d

Browse files
feat(tests): make tests dependent on MAX_CODE_SIZE now depend on the fork-based max code size
1 parent 36d84cb commit 8a40f0d

File tree

2 files changed

+50
-47
lines changed

2 files changed

+50
-47
lines changed

tests/zkevm/test_worst_compute.py

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
REFERENCE_SPEC_GIT_PATH = "TODO"
4040
REFERENCE_SPEC_VERSION = "TODO"
4141

42-
MAX_CODE_SIZE = 24 * 1024
4342
KECCAK_RATE = 136
4443

4544

@@ -342,6 +341,8 @@ def test_worst_keccak(
342341
gsc = fork.gas_costs()
343342
mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()
344343

344+
max_code_size = fork.max_code_size()
345+
345346
# Discover the optimal input size to maximize keccak-permutations, not keccak calls.
346347
# The complication of the discovery arises from the non-linear gas cost of memory expansion.
347348
max_keccak_perm_per_block = 0
@@ -372,18 +373,18 @@ def test_worst_keccak(
372373
# The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
373374
#
374375
# 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.
376377
# Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
377378
# TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
378379
# attack, whenever this is fixed adjust accordingly.
379380
start_code = Op.JUMPDEST + Op.PUSH20[optimal_input_length]
380381
loop_code = Op.POP(Op.SHA3(Op.PUSH0, Op.DUP1))
381382
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)
383384
code = start_code + (loop_code * max_iters_loop) + end_code
384-
if len(code) > MAX_CODE_SIZE:
385+
if len(code) > max_code_size:
385386
# 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}")
387388

388389
code_address = pre.deploy_contract(code=bytes(code))
389390

@@ -730,37 +731,37 @@ def test_worst_precompile_fixed_cost(
730731
)
731732

732733

733-
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode):
734+
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode, fork: Fork):
734735
"""Create a code loop that calls a precompile with the given calldata."""
736+
max_code_size = fork.max_code_size()
737+
735738
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
736739
jumpdest = Op.JUMPDEST
737740
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(
739742
attack_block
740743
)
741744
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:
743746
# 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}")
745748

746749
return code
747750

748751

749752
@pytest.mark.zkevm
750753
@pytest.mark.valid_from("Cancun")
751754
@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):
756756
"""Test running a JUMP-intensive contract."""
757757
env = Environment()
758+
max_code_size = fork.max_code_size()
758759

759760
def jump_seq():
760761
return Op.JUMP(Op.ADD(Op.PC, 1)) + Op.JUMPDEST
761762

762763
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
764765

765766
# Create and deploy the jump-intensive contract
766767
jumps_code = sum([jump_seq() for _ in range(seqs_per_call)])
@@ -787,15 +788,13 @@ def jump_seq():
787788
@pytest.mark.zkevm
788789
@pytest.mark.valid_from("Cancun")
789790
@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):
794792
"""Test running a JUMPDEST-intensive contract."""
795793
env = Environment()
794+
max_code_size = fork.max_code_size()
796795

797796
# 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)
799798
jumpdests_address = pre.deploy_contract(code=bytes(jumpdests_code))
800799

801800
# Call the contract repeatedly until gas runs out.
@@ -940,26 +939,24 @@ def test_worst_jumpdests(
940939
ids=lambda param: "" if isinstance(param, tuple) else param,
941940
)
942941
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]
947943
):
948944
"""
949945
Test running a block with as many binary instructions (takes two args, produces one value)
950946
as possible. The execution starts with two initial values on the stack, and the stack is
951947
balanced by the DUP2 instruction.
952948
"""
953949
env = Environment()
950+
max_code_size = fork.max_code_size()
954951

955952
tx_data = b"".join(arg.to_bytes(32, byteorder="big") for arg in opcode_args)
956953

957954
code_prefix = Op.JUMPDEST + Op.CALLDATALOAD(0) + Op.CALLDATALOAD(32)
958955
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)
960957
code_body = (Op.DUP2 + opcode) * (code_body_len // 2)
961958
code = code_prefix + code_body + code_suffix
962-
assert len(code) == MAX_CODE_SIZE - 1
959+
assert len(code) == max_code_size - 1
963960

964961
tx = Transaction(
965962
to=pre.deploy_contract(code=code),
@@ -978,23 +975,20 @@ def test_worst_binop_simple(
978975

979976
@pytest.mark.valid_from("Cancun")
980977
@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):
986979
"""
987980
Test running a block with as many unary instructions (takes one arg, produces one value)
988981
as possible.
989982
"""
990983
env = Environment()
984+
max_code_size = fork.max_code_size()
991985

992986
code_prefix = Op.JUMPDEST + Op.PUSH0 # Start with the arg 0.
993987
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)
995989
code_body = opcode * code_body_len
996990
code = code_prefix + code_body + code_suffix
997-
assert len(code) == MAX_CODE_SIZE
991+
assert len(code) == max_code_size
998992

999993
tx = Transaction(
1000994
to=pre.deploy_contract(code=code),
@@ -1015,13 +1009,15 @@ def test_worst_unop(
10151009
def test_worst_shifts(
10161010
state_test: StateTestFiller,
10171011
pre: Alloc,
1012+
fork: Fork,
10181013
shift_right: Op,
10191014
):
10201015
"""
10211016
Test running a block with as many shift instructions with non-trivial arguments.
10221017
This test generates left-right pairs of shifts to avoid zeroing the argument.
10231018
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
10241019
"""
1020+
max_code_size = fork.max_code_size()
10251021

10261022
def to_signed(x):
10271023
return x if x < 2**255 else x - 2**256
@@ -1055,7 +1051,7 @@ def sar(x, s):
10551051

10561052
code_prefix = sum(Op.PUSH1[sh] for sh in shift_amounts) + Op.JUMPDEST + Op.CALLDATALOAD(0)
10571053
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)
10591055

10601056
def select_shift_amount(shift_fn, v):
10611057
"""Select a shift amount that will produce a non-zero result."""
@@ -1075,7 +1071,7 @@ def select_shift_amount(shift_fn, v):
10751071
code_body += make_dup(len(shift_amounts) - i) + shift_right
10761072

10771073
code = code_prefix + code_body + code_suffix
1078-
assert len(code) == MAX_CODE_SIZE - 2
1074+
assert len(code) == max_code_size - 2
10791075

10801076
env = Environment()
10811077

@@ -1113,14 +1109,15 @@ def test_worst_blobhash(
11131109
):
11141110
"""Test running a block with as many BLOBHASH instructions as possible."""
11151111
env = Environment()
1112+
max_code_size = fork.max_code_size()
11161113

11171114
code_prefix = Op.PUSH1(blob_index) + Op.JUMPDEST
11181115
code_suffix = Op.JUMP(len(code_prefix) - 1)
11191116
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)
11211118
code_body = loop_iter * code_body_len
11221119
code = code_prefix + code_body + code_suffix
1123-
assert len(code) <= MAX_CODE_SIZE
1120+
assert len(code) <= max_code_size
11241121

11251122
tx_type = TransactionType.LEGACY
11261123
blob_versioned_hashes = None
@@ -1156,6 +1153,7 @@ def test_worst_blobhash(
11561153
def test_worst_mod(
11571154
state_test: StateTestFiller,
11581155
pre: Alloc,
1156+
fork: Fork,
11591157
mod_bits: int,
11601158
op: Op,
11611159
):
@@ -1169,6 +1167,8 @@ def test_worst_mod(
11691167
The order of accessing the numerators is selected in a way the mod value remains in the range
11701168
as long as possible.
11711169
"""
1170+
max_code_size = fork.max_code_size()
1171+
11721172
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
11731173
# just the SMOD implementation will have to additionally handle the sign bits.
11741174
# The result stays negative.
@@ -1242,7 +1242,7 @@ def test_worst_mod(
12421242
code_constant_pool = sum((Op.PUSH32[n] for n in numerators), Bytecode())
12431243
code_prefix = code_constant_pool + Op.JUMPDEST
12441244
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)
12461246
code_segment = (
12471247
Op.CALLDATALOAD(0) + sum(make_dup(len(numerators) - i) + op for i in indexes) + Op.POP
12481248
)
@@ -1252,7 +1252,7 @@ def test_worst_mod(
12521252
+ sum(code_segment for _ in range(code_body_len // len(code_segment)))
12531253
+ code_suffix
12541254
)
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
12561256

12571257
env = Environment()
12581258

@@ -1280,13 +1280,15 @@ def test_worst_mod(
12801280
def test_worst_memory_access(
12811281
state_test: StateTestFiller,
12821282
pre: Alloc,
1283+
fork: Fork,
12831284
opcode: Op,
12841285
offset: int,
12851286
offset_initialized: bool,
12861287
big_memory_expansion: bool,
12871288
):
12881289
"""Test running a block with as many memory access instructions as possible."""
12891290
env = Environment()
1291+
max_code_size = fork.max_code_size()
12901292

12911293
mem_exp_code = Op.MSTORE8(10 * 1024, 1) if big_memory_expansion else Bytecode()
12921294
offset_set_code = Op.MSTORE(offset, 43) if offset_initialized else Bytecode()
@@ -1296,10 +1298,10 @@ def test_worst_memory_access(
12961298

12971299
loop_iter = Op.POP(Op.MLOAD(Op.DUP1)) if opcode == Op.MLOAD else opcode(Op.DUP2, Op.DUP2)
12981300

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)
13001302
code_body = loop_iter * code_body_len
13011303
code = code_prefix + code_body + code_suffix
1302-
assert len(code) <= MAX_CODE_SIZE
1304+
assert len(code) <= max_code_size
13031305

13041306
tx = Transaction(
13051307
to=pre.deploy_contract(code=code),
@@ -1446,14 +1448,15 @@ def test_worst_calldataload(
14461448
):
14471449
"""Test running a block with as many CALLDATALOAD as possible."""
14481450
env = Environment()
1451+
max_code_size = fork.max_code_size()
14491452

14501453
code_prefix = Op.PUSH0 + Op.JUMPDEST
14511454
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)
14531456
code_loop_iter = Op.CALLDATALOAD
14541457
code_body = code_loop_iter * (code_body_len // len(code_loop_iter))
14551458
code = code_prefix + code_body + code_suffix
1456-
assert len(code) <= MAX_CODE_SIZE
1459+
assert len(code) <= max_code_size
14571460

14581461
tx = Transaction(
14591462
to=pre.deploy_contract(code=code),

tests/zkevm/test_worst_stateful_opcodes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
REFERENCE_SPEC_GIT_PATH = "TODO"
2727
REFERENCE_SPEC_VERSION = "TODO"
2828

29-
MAX_CODE_SIZE = 24 * 1024
30-
3129

3230
@pytest.mark.valid_from("Cancun")
3331
@pytest.mark.parametrize(
@@ -137,11 +135,13 @@ def test_worst_address_state_cold(
137135
def test_worst_address_state_warm(
138136
state_test: StateTestFiller,
139137
pre: Alloc,
138+
fork: Fork,
140139
opcode: Op,
141140
absent_target: bool,
142141
):
143142
"""Test running a block with as many stateful opcodes doing warm access for an account."""
144143
env = Environment(gas_limit=100_000_000_000)
144+
max_code_size = fork.max_code_size()
145145
attack_gas_limit = Environment().gas_limit
146146

147147
# Setup
@@ -157,13 +157,13 @@ def test_worst_address_state_warm(
157157
jumpdest = Op.JUMPDEST
158158
jump_back = Op.JUMP(len(prep))
159159
iter_block = Op.POP(opcode(address=Op.MLOAD(0)))
160-
max_iters_loop = (MAX_CODE_SIZE - len(prep) - len(jumpdest) - len(jump_back)) // len(
160+
max_iters_loop = (max_code_size - len(prep) - len(jumpdest) - len(jump_back)) // len(
161161
iter_block
162162
)
163163
op_code = prep + jumpdest + sum([iter_block] * max_iters_loop) + jump_back
164-
if len(op_code) > MAX_CODE_SIZE:
164+
if len(op_code) > max_code_size:
165165
# Must never happen, but keep it as a sanity check.
166-
raise ValueError(f"Code size {len(op_code)} exceeds maximum code size {MAX_CODE_SIZE}")
166+
raise ValueError(f"Code size {len(op_code)} exceeds maximum code size {max_code_size}")
167167
op_address = pre.deploy_contract(code=op_code)
168168
tx = Transaction(
169169
to=op_address,

0 commit comments

Comments
 (0)