Skip to content

Commit 5e823ff

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

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
@@ -40,7 +40,6 @@
4040
REFERENCE_SPEC_GIT_PATH = "TODO"
4141
REFERENCE_SPEC_VERSION = "TODO"
4242

43-
MAX_CODE_SIZE = 24 * 1024
4443
KECCAK_RATE = 136
4544

4645

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

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

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

@@ -731,37 +732,37 @@ def test_worst_precompile_fixed_cost(
731732
)
732733

733734

734-
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode):
735+
def code_loop_precompile_call(calldata: Bytecode, attack_block: Bytecode, fork: Fork):
735736
"""Create a code loop that calls a precompile with the given calldata."""
737+
max_code_size = fork.max_code_size()
738+
736739
# The attack contract is: CALLDATA_PREP + #JUMPDEST + [attack_block]* + JUMP(#)
737740
jumpdest = Op.JUMPDEST
738741
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(
740743
attack_block
741744
)
742745
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:
744747
# 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}")
746749

747750
return code
748751

749752

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

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

763764
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
765766

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

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

802801
# Call the contract repeatedly until gas runs out.
@@ -961,26 +960,24 @@ def test_worst_jumpdests(
961960
ids=lambda param: "" if isinstance(param, tuple) else param,
962961
)
963962
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]
968964
):
969965
"""
970966
Test running a block with as many binary instructions (takes two args, produces one value)
971967
as possible. The execution starts with two initial values on the stack, and the stack is
972968
balanced by the DUP2 instruction.
973969
"""
974970
env = Environment()
971+
max_code_size = fork.max_code_size()
975972

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

978975
code_prefix = Op.JUMPDEST + Op.CALLDATALOAD(0) + Op.CALLDATALOAD(32)
979976
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)
981978
code_body = (Op.DUP2 + opcode) * (code_body_len // 2)
982979
code = code_prefix + code_body + code_suffix
983-
assert len(code) == MAX_CODE_SIZE - 1
980+
assert len(code) == max_code_size - 1
984981

985982
tx = Transaction(
986983
to=pre.deploy_contract(code=code),
@@ -999,23 +996,20 @@ def test_worst_binop_simple(
999996

1000997
@pytest.mark.valid_from("Cancun")
1001998
@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):
10071000
"""
10081001
Test running a block with as many unary instructions (takes one arg, produces one value)
10091002
as possible.
10101003
"""
10111004
env = Environment()
1005+
max_code_size = fork.max_code_size()
10121006

10131007
code_prefix = Op.JUMPDEST + Op.PUSH0 # Start with the arg 0.
10141008
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)
10161010
code_body = opcode * code_body_len
10171011
code = code_prefix + code_body + code_suffix
1018-
assert len(code) == MAX_CODE_SIZE
1012+
assert len(code) == max_code_size
10191013

10201014
tx = Transaction(
10211015
to=pre.deploy_contract(code=code),
@@ -1036,13 +1030,15 @@ def test_worst_unop(
10361030
def test_worst_shifts(
10371031
state_test: StateTestFiller,
10381032
pre: Alloc,
1033+
fork: Fork,
10391034
shift_right: Op,
10401035
):
10411036
"""
10421037
Test running a block with as many shift instructions with non-trivial arguments.
10431038
This test generates left-right pairs of shifts to avoid zeroing the argument.
10441039
The shift amounts are randomly pre-selected from the constant pool of 15 values on the stack.
10451040
"""
1041+
max_code_size = fork.max_code_size()
10461042

10471043
def to_signed(x):
10481044
return x if x < 2**255 else x - 2**256
@@ -1076,7 +1072,7 @@ def sar(x, s):
10761072

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

10811077
def select_shift_amount(shift_fn, v):
10821078
"""Select a shift amount that will produce a non-zero result."""
@@ -1096,7 +1092,7 @@ def select_shift_amount(shift_fn, v):
10961092
code_body += make_dup(len(shift_amounts) - i) + shift_right
10971093

10981094
code = code_prefix + code_body + code_suffix
1099-
assert len(code) == MAX_CODE_SIZE - 2
1095+
assert len(code) == max_code_size - 2
11001096

11011097
env = Environment()
11021098

@@ -1134,14 +1130,15 @@ def test_worst_blobhash(
11341130
):
11351131
"""Test running a block with as many BLOBHASH instructions as possible."""
11361132
env = Environment()
1133+
max_code_size = fork.max_code_size()
11371134

11381135
code_prefix = Op.PUSH1(blob_index) + Op.JUMPDEST
11391136
code_suffix = Op.JUMP(len(code_prefix) - 1)
11401137
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)
11421139
code_body = loop_iter * code_body_len
11431140
code = code_prefix + code_body + code_suffix
1144-
assert len(code) <= MAX_CODE_SIZE
1141+
assert len(code) <= max_code_size
11451142

11461143
tx_type = TransactionType.LEGACY
11471144
blob_versioned_hashes = None
@@ -1177,6 +1174,7 @@ def test_worst_blobhash(
11771174
def test_worst_mod(
11781175
state_test: StateTestFiller,
11791176
pre: Alloc,
1177+
fork: Fork,
11801178
mod_bits: int,
11811179
op: Op,
11821180
):
@@ -1190,6 +1188,8 @@ def test_worst_mod(
11901188
The order of accessing the numerators is selected in a way the mod value remains in the range
11911189
as long as possible.
11921190
"""
1191+
max_code_size = fork.max_code_size()
1192+
11931193
# For SMOD we negate both numerator and modulus. The underlying computation is the same,
11941194
# just the SMOD implementation will have to additionally handle the sign bits.
11951195
# The result stays negative.
@@ -1263,7 +1263,7 @@ def test_worst_mod(
12631263
code_constant_pool = sum((Op.PUSH32[n] for n in numerators), Bytecode())
12641264
code_prefix = code_constant_pool + Op.JUMPDEST
12651265
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)
12671267
code_segment = (
12681268
Op.CALLDATALOAD(0) + sum(make_dup(len(numerators) - i) + op for i in indexes) + Op.POP
12691269
)
@@ -1273,7 +1273,7 @@ def test_worst_mod(
12731273
+ sum(code_segment for _ in range(code_body_len // len(code_segment)))
12741274
+ code_suffix
12751275
)
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
12771277

12781278
env = Environment()
12791279

@@ -1301,13 +1301,15 @@ def test_worst_mod(
13011301
def test_worst_memory_access(
13021302
state_test: StateTestFiller,
13031303
pre: Alloc,
1304+
fork: Fork,
13041305
opcode: Op,
13051306
offset: int,
13061307
offset_initialized: bool,
13071308
big_memory_expansion: bool,
13081309
):
13091310
"""Test running a block with as many memory access instructions as possible."""
13101311
env = Environment()
1312+
max_code_size = fork.max_code_size()
13111313

13121314
mem_exp_code = Op.MSTORE8(10 * 1024, 1) if big_memory_expansion else Bytecode()
13131315
offset_set_code = Op.MSTORE(offset, 43) if offset_initialized else Bytecode()
@@ -1317,10 +1319,10 @@ def test_worst_memory_access(
13171319

13181320
loop_iter = Op.POP(Op.MLOAD(Op.DUP1)) if opcode == Op.MLOAD else opcode(Op.DUP2, Op.DUP2)
13191321

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)
13211323
code_body = loop_iter * code_body_len
13221324
code = code_prefix + code_body + code_suffix
1323-
assert len(code) <= MAX_CODE_SIZE
1325+
assert len(code) <= max_code_size
13241326

13251327
tx = Transaction(
13261328
to=pre.deploy_contract(code=code),
@@ -1570,14 +1572,15 @@ def test_worst_calldataload(
15701572
):
15711573
"""Test running a block with as many CALLDATALOAD as possible."""
15721574
env = Environment()
1575+
max_code_size = fork.max_code_size()
15731576

15741577
code_prefix = Op.PUSH0 + Op.JUMPDEST
15751578
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)
15771580
code_loop_iter = Op.CALLDATALOAD
15781581
code_body = code_loop_iter * (code_body_len // len(code_loop_iter))
15791582
code = code_prefix + code_body + code_suffix
1580-
assert len(code) <= MAX_CODE_SIZE
1583+
assert len(code) <= max_code_size
15811584

15821585
tx = Transaction(
15831586
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)