Skip to content

Commit d5b19d2

Browse files
feat(tests): make tests dependent on MAX_CODE_SIZE now depend on the fork-based max code size
1 parent fd4d341 commit d5b19d2

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
@@ -38,7 +38,6 @@
3838
REFERENCE_SPEC_GIT_PATH = "TODO"
3939
REFERENCE_SPEC_VERSION = "TODO"
4040

41-
MAX_CODE_SIZE = 24 * 1024
4241
KECCAK_RATE = 136
4342

4443

@@ -73,6 +72,8 @@ def test_worst_keccak(
7372
gsc = fork.gas_costs()
7473
mem_exp_gas_calculator = fork.memory_expansion_gas_calculator()
7574

75+
max_code_size = fork.max_code_size()
76+
7677
# Discover the optimal input size to maximize keccak-permutations, not keccak calls.
7778
# The complication of the discovery arises from the non-linear gas cost of memory expansion.
7879
max_keccak_perm_per_block = 0
@@ -103,18 +104,18 @@ def test_worst_keccak(
103104
# The loop structure is: JUMPDEST + [attack iteration] + PUSH0 + JUMP
104105
#
105106
# 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.
107108
# Denominator = (PUSHN + PUSH1 + KECCAK256 + POP) + PUSH1_DATA + PUSHN_DATA
108109
# TODO: the testing framework uses PUSH1(0) instead of PUSH0 which is suboptimal for the
109110
# attack, whenever this is fixed adjust accordingly.
110111
start_code = Op.JUMPDEST + Op.PUSH20[optimal_input_length]
111112
loop_code = Op.POP(Op.SHA3(Op.PUSH0, Op.DUP1))
112113
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)
114115
code = start_code + (loop_code * max_iters_loop) + end_code
115-
if len(code) > MAX_CODE_SIZE:
116+
if len(code) > max_code_size:
116117
# 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}")
118119

119120
code_address = pre.deploy_contract(code=bytes(code))
120121

@@ -461,37 +462,37 @@ def test_worst_precompile_fixed_cost(
461462
)
462463

463464

464-
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode):
465+
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode, fork: Fork):
465466
"""Create a code loop that calls a precompile with the given calldata."""
467+
max_code_size = fork.max_code_size()
468+
466469
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
467470
jumpdest = Op.JUMPDEST
468471
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(
470473
attack_block
471474
)
472475
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:
474477
# 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}")
476479

477480
return code
478481

479482

480483
@pytest.mark.zkevm
481484
@pytest.mark.valid_from("Cancun")
482485
@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):
487487
"""Test running a JUMP-intensive contract."""
488488
env = Environment()
489+
max_code_size = fork.max_code_size()
489490

490491
def jump_seq():
491492
return Op.JUMP(Op.ADD(Op.PC, 1)) + Op.JUMPDEST
492493

493494
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
495496

496497
# Create and deploy the jump-intensive contract
497498
jumps_code = sum([jump_seq() for _ in range(seqs_per_call)])
@@ -518,15 +519,13 @@ def jump_seq():
518519
@pytest.mark.zkevm
519520
@pytest.mark.valid_from("Cancun")
520521
@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):
525523
"""Test running a JUMPDEST-intensive contract."""
526524
env = Environment()
525+
max_code_size = fork.max_code_size()
527526

528527
# 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)
530529
jumpdests_address = pre.deploy_contract(code=bytes(jumpdests_code))
531530

532531
# Call the contract repeatedly until gas runs out.
@@ -671,26 +670,24 @@ def test_worst_jumpdests(
671670
ids=lambda param: "" if isinstance(param, tuple) else param,
672671
)
673672
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]
678674
):
679675
"""
680676
Test running a block with as many binary instructions (takes two args, produces one value)
681677
as possible. The execution starts with two initial values on the stack, and the stack is
682678
balanced by the DUP2 instruction.
683679
"""
684680
env = Environment()
681+
max_code_size = fork.max_code_size()
685682

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

688685
code_prefix = Op.JUMPDEST + Op.CALLDATALOAD(0) + Op.CALLDATALOAD(32)
689686
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)
691688
code_body = (Op.DUP2 + opcode) * (code_body_len // 2)
692689
code = code_prefix + code_body + code_suffix
693-
assert len(code) == MAX_CODE_SIZE - 1
690+
assert len(code) == max_code_size - 1
694691

695692
tx = Transaction(
696693
to=pre.deploy_contract(code=code),
@@ -709,23 +706,20 @@ def test_worst_binop_simple(
709706

710707
@pytest.mark.valid_from("Cancun")
711708
@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):
717710
"""
718711
Test running a block with as many unary instructions (takes one arg, produces one value)
719712
as possible.
720713
"""
721714
env = Environment()
715+
max_code_size = fork.max_code_size()
722716

723717
code_prefix = Op.JUMPDEST + Op.PUSH0 # Start with the arg 0.
724718
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)
726720
code_body = opcode * code_body_len
727721
code = code_prefix + code_body + code_suffix
728-
assert len(code) == MAX_CODE_SIZE
722+
assert len(code) == max_code_size
729723

730724
tx = Transaction(
731725
to=pre.deploy_contract(code=code),
@@ -746,13 +740,15 @@ def test_worst_unop(
746740
def test_worst_shifts(
747741
state_test: StateTestFiller,
748742
pre: Alloc,
743+
fork: Fork,
749744
shift_right: Op,
750745
):
751746
"""
752747
Test running a block with as many shift instructions with non-trivial arguments.
753748
This test generates left-right pairs of shifts to avoid zeroing the argument.
754749
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
755750
"""
751+
max_code_size = fork.max_code_size()
756752

757753
def to_signed(x):
758754
return x if x < 2**255 else x - 2**256
@@ -786,7 +782,7 @@ def sar(x, s):
786782

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

791787
def select_shift_amount(shift_fn, v):
792788
"""Select a shift amount that will produce a non-zero result."""
@@ -806,7 +802,7 @@ def select_shift_amount(shift_fn, v):
806802
code_body += make_dup(len(shift_amounts) - i) + shift_right
807803

808804
code = code_prefix + code_body + code_suffix
809-
assert len(code) == MAX_CODE_SIZE - 2
805+
assert len(code) == max_code_size - 2
810806

811807
env = Environment()
812808

@@ -844,14 +840,15 @@ def test_worst_blobhash(
844840
):
845841
"""Test running a block with as many BLOBHASH instructions as possible."""
846842
env = Environment()
843+
max_code_size = fork.max_code_size()
847844

848845
code_prefix = Op.PUSH1(blob_index) + Op.JUMPDEST
849846
code_suffix = Op.JUMP(len(code_prefix) - 1)
850847
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)
852849
code_body = loop_iter * code_body_len
853850
code = code_prefix + code_body + code_suffix
854-
assert len(code) <= MAX_CODE_SIZE
851+
assert len(code) <= max_code_size
855852

856853
tx_type = TransactionType.LEGACY
857854
blob_versioned_hashes = None
@@ -887,6 +884,7 @@ def test_worst_blobhash(
887884
def test_worst_mod(
888885
state_test: StateTestFiller,
889886
pre: Alloc,
887+
fork: Fork,
890888
mod_bits: int,
891889
op: Op,
892890
):
@@ -900,6 +898,8 @@ def test_worst_mod(
900898
The order of accessing the numerators is selected in a way the mod value remains in the range
901899
as long as possible.
902900
"""
901+
max_code_size = fork.max_code_size()
902+
903903
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
904904
# just the SMOD implementation will have to additionally handle the sign bits.
905905
# The result stays negative.
@@ -973,7 +973,7 @@ def test_worst_mod(
973973
code_constant_pool = sum((Op.PUSH32[n] for n in numerators), Bytecode())
974974
code_prefix = code_constant_pool + Op.JUMPDEST
975975
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)
977977
code_segment = (
978978
Op.CALLDATALOAD(0) + sum(make_dup(len(numerators) - i) + op for i in indexes) + Op.POP
979979
)
@@ -983,7 +983,7 @@ def test_worst_mod(
983983
+ sum(code_segment for _ in range(code_body_len // len(code_segment)))
984984
+ code_suffix
985985
)
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
987987

988988
env = Environment()
989989

@@ -1011,13 +1011,15 @@ def test_worst_mod(
10111011
def test_worst_memory_access(
10121012
state_test: StateTestFiller,
10131013
pre: Alloc,
1014+
fork: Fork,
10141015
opcode: Op,
10151016
offset: int,
10161017
offset_initialized: bool,
10171018
big_memory_expansion: bool,
10181019
):
10191020
"""Test running a block with as many memory access instructions as possible."""
10201021
env = Environment()
1022+
max_code_size = fork.max_code_size()
10211023

10221024
mem_exp_code = Op.MSTORE8(10 * 1024, 1) if big_memory_expansion else Bytecode()
10231025
offset_set_code = Op.MSTORE(offset, 43) if offset_initialized else Bytecode()
@@ -1027,10 +1029,10 @@ def test_worst_memory_access(
10271029

10281030
loop_iter = Op.POP(Op.MLOAD(Op.DUP1)) if opcode == Op.MLOAD else opcode(Op.DUP2, Op.DUP2)
10291031

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)
10311033
code_body = loop_iter * code_body_len
10321034
code = code_prefix + code_body + code_suffix
1033-
assert len(code) <= MAX_CODE_SIZE
1035+
assert len(code) <= max_code_size
10341036

10351037
tx = Transaction(
10361038
to=pre.deploy_contract(code=code),
@@ -1177,14 +1179,15 @@ def test_worst_calldataload(
11771179
):
11781180
"""Test running a block with as many CALLDATALOAD as possible."""
11791181
env = Environment()
1182+
max_code_size = fork.max_code_size()
11801183

11811184
code_prefix = Op.PUSH0 + Op.JUMPDEST
11821185
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)
11841187
code_loop_iter = Op.CALLDATALOAD
11851188
code_body = code_loop_iter * (code_body_len // len(code_loop_iter))
11861189
code = code_prefix + code_body + code_suffix
1187-
assert len(code) <= MAX_CODE_SIZE
1190+
assert len(code) <= max_code_size
11881191

11891192
tx = Transaction(
11901193
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)