Skip to content

Commit 43e7091

Browse files
committed
refactor: centralize memory layout constants in helpers.py
Co-Authored-By: Claude claude-opus-4-6
1 parent 7a3510a commit 43e7091

File tree

2 files changed

+59
-39
lines changed

2 files changed

+59
-39
lines changed

tests/monad_eight/staking_precompile/helpers.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
from execution_testing import Bytecode, Op
44
from execution_testing.forks.helpers import Fork
55

6+
# Memory layout for precompile call tests.
7+
# MSTORE writes a 32-byte word; the 4-byte selector sits at the
8+
# rightmost bytes of that word.
9+
WRONG_SEL_MSTORE_OFFSET = 0
10+
CORRECT_SEL_MSTORE_OFFSET = 32
11+
12+
# Where the 4-byte selectors actually start in memory:
13+
WRONG_SEL_ARGS_OFFSET = WRONG_SEL_MSTORE_OFFSET + 28
14+
CORRECT_SEL_ARGS_OFFSET = CORRECT_SEL_MSTORE_OFFSET + 28
15+
616

717
def generous_gas(fork: Fork) -> int:
818
"""Return generous parametrized gas to always be enough."""
@@ -27,13 +37,32 @@ def tx_calldata(selector: int, calldata_size: int) -> bytes:
2737
return sel_bytes + b"\x00" * max(0, calldata_size - 4)
2838

2939

40+
def calldata_mem_end(calldata_size: int) -> int:
41+
"""
42+
Return safe first memory offset past all written regions.
43+
44+
Accounts for EXTRA_CALLDATA (+1 byte beyond calldata_size)
45+
and both MSTORE regions.
46+
"""
47+
return (
48+
max(
49+
CORRECT_SEL_ARGS_OFFSET + calldata_size,
50+
CORRECT_SEL_MSTORE_OFFSET + 32,
51+
)
52+
+ 1
53+
)
54+
55+
3056
def build_calldata(selector: int, calldata_size: int) -> Bytecode:
3157
"""
32-
Build bytecode that stores a selector and padding in memory.
58+
Build bytecode that stores a selector in memory.
3359
34-
Place the 4-byte selector at mem[60:64] and assume mem[64:..] has
35-
rest of calldata
60+
Place the 4-byte selector at
61+
mem[CORRECT_SEL_ARGS_OFFSET:CORRECT_SEL_ARGS_OFFSET+4]
62+
and leave the rest zero-initialized for calldata args.
3663
"""
37-
selector_calldata_offset = 32
38-
code = Op.MSTORE(selector_calldata_offset, selector)
64+
# Not used b/c input is zero beyond the selector;
65+
# left here only for clarity
66+
del calldata_size
67+
code = Op.MSTORE(CORRECT_SEL_MSTORE_OFFSET, selector)
3968
return code

tests/monad_eight/staking_precompile/test_precompile_call.py

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,15 @@
3232
from execution_testing.forks.helpers import Fork
3333
from execution_testing.test_types.receipt_types import TransactionReceipt
3434

35-
from .helpers import build_calldata, generous_gas, tx_calldata
35+
from .helpers import (
36+
CORRECT_SEL_ARGS_OFFSET,
37+
WRONG_SEL_ARGS_OFFSET,
38+
WRONG_SEL_MSTORE_OFFSET,
39+
build_calldata,
40+
calldata_mem_end,
41+
generous_gas,
42+
tx_calldata,
43+
)
3644
from .spec import (
3745
ALL_FUNCTIONS,
3846
ERROR_INPUT_TOO_SHORT,
@@ -61,11 +69,6 @@
6169
slot_all_gas_consumed = 0x6
6270

6371

64-
def _calldata_mem_end(calldata_size: int) -> int:
65-
"""Return the first memory offset after build_calldata's writes."""
66-
return 60 + calldata_size + 1
67-
68-
6972
def _mload_of(msg: bytes) -> int:
7073
"""
7174
Compute the MLOAD uint256 value after a raw error message is
@@ -350,32 +353,24 @@ def scenario_call_code(
350353
"""
351354
scenario_set = set(scenarios)
352355

353-
# Memory layout: non-overlapping buffers
354-
# MSTORE(0, 0xDEADBEEF) -> wrong selector at mem[28:32]
355-
# build_calldata(selector, size) -> selector at mem[60:64]
356-
wrong_sel_args_offset = 28
357-
correct_sel_args_offset = 60
358-
359-
# NOTE: in case of wrong selector scenario, we do not include any
360-
# extra calldata for (non-existent) function arguments
361356
setup: Bytecode = build_calldata(
362357
func.selector, func.calldata_size
363-
) + Op.MSTORE(0, 0xDEADBEEF)
358+
) + Op.MSTORE(WRONG_SEL_MSTORE_OFFSET, 0xDEADBEEF)
364359

365360
if CallScenario.WRONG_SELECTOR in scenario_set:
366-
args_offset = wrong_sel_args_offset
361+
args_offset = WRONG_SEL_ARGS_OFFSET
367362
args_size = 4
368363
elif CallScenario.TRUNCATED_SELECTOR in scenario_set:
369-
args_offset = correct_sel_args_offset
364+
args_offset = CORRECT_SEL_ARGS_OFFSET
370365
args_size = 3
371366
elif CallScenario.SHORT_CALLDATA in scenario_set:
372-
args_offset = correct_sel_args_offset
367+
args_offset = CORRECT_SEL_ARGS_OFFSET
373368
args_size = func.calldata_size - 1
374369
elif CallScenario.EXTRA_CALLDATA in scenario_set:
375-
args_offset = correct_sel_args_offset
370+
args_offset = CORRECT_SEL_ARGS_OFFSET
376371
args_size = func.calldata_size + 1
377372
else:
378-
args_offset = correct_sel_args_offset
373+
args_offset = CORRECT_SEL_ARGS_OFFSET
379374
args_size = func.calldata_size
380375

381376
if ret_size > 0:
@@ -475,7 +470,7 @@ def test_input_size(
475470
Op.CALL(
476471
gas=gas,
477472
address=STAKING_PRECOMPILE,
478-
args_offset=60,
473+
args_offset=CORRECT_SEL_ARGS_OFFSET,
479474
args_size=input_size,
480475
ret_offset=0,
481476
ret_size=32,
@@ -548,7 +543,7 @@ def test_selector(
548543
Op.CALL(
549544
gas=gas,
550545
address=STAKING_PRECOMPILE,
551-
args_offset=60,
546+
args_offset=CORRECT_SEL_ARGS_OFFSET,
552547
args_size=args_size,
553548
ret_offset=0,
554549
ret_size=32,
@@ -606,7 +601,7 @@ def test_gas(
606601
Op.CALL(
607602
gas=gas,
608603
address=STAKING_PRECOMPILE,
609-
args_offset=60,
604+
args_offset=CORRECT_SEL_ARGS_OFFSET,
610605
args_size=func.calldata_size,
611606
ret_offset=0,
612607
ret_size=32,
@@ -666,7 +661,7 @@ def test_call_opcodes(
666661
call_opcode(
667662
gas=func.gas_cost + 10000,
668663
address=STAKING_PRECOMPILE,
669-
args_offset=60,
664+
args_offset=CORRECT_SEL_ARGS_OFFSET,
670665
args_size=func.calldata_size,
671666
ret_offset=0,
672667
ret_size=32,
@@ -718,8 +713,7 @@ def test_revert_returns(
718713
"""
719714
Test return data on success and on each revert reason.
720715
"""
721-
mem_end = _calldata_mem_end(func.calldata_size)
722-
ret_offset = max(mem_end, 96)
716+
ret_offset = calldata_mem_end(func.calldata_size)
723717
rdc_offset = ret_offset + 32
724718

725719
delegating_eoa: Address | None = None
@@ -875,8 +869,7 @@ def test_call_with_value(
875869
Payable functions accept value; non-payable functions revert with
876870
"value is nonzero".
877871
"""
878-
mem_end = _calldata_mem_end(func.calldata_size)
879-
ret_offset = max(mem_end, 96)
872+
ret_offset = calldata_mem_end(func.calldata_size)
880873
rdc_offset = ret_offset + 32
881874

882875
contract = (
@@ -887,7 +880,7 @@ def test_call_with_value(
887880
gas=func.gas_cost + 10000,
888881
address=STAKING_PRECOMPILE,
889882
value=value,
890-
args_offset=60,
883+
args_offset=CORRECT_SEL_ARGS_OFFSET,
891884
args_size=func.calldata_size,
892885
ret_offset=ret_offset,
893886
ret_size=32,
@@ -969,8 +962,7 @@ def test_check_order(
969962
Each combination triggers exactly two failure causes. The test
970963
derives the expected outcome from the higher-priority failure.
971964
"""
972-
mem_end = _calldata_mem_end(func.calldata_size)
973-
ret_offset = max(mem_end, 96)
965+
ret_offset = calldata_mem_end(func.calldata_size)
974966
rdc_offset = ret_offset + 32
975967

976968
delegating_eoa: Address | None = None
@@ -1083,8 +1075,7 @@ def test_check_order_triple(
10831075
if not _pairwise_compatible(tuple(normalized)):
10841076
pytest.skip("normalized scenarios are incompatible")
10851077

1086-
mem_end = _calldata_mem_end(func.calldata_size)
1087-
ret_offset = max(mem_end, 96)
1078+
ret_offset = calldata_mem_end(func.calldata_size)
10881079
rdc_offset = ret_offset + 32
10891080

10901081
scenarios = {scenario1, scenario2, scenario3}
@@ -1439,7 +1430,7 @@ def test_syscall_rejected(
14391430
Op.CALL(
14401431
gas=100000,
14411432
address=STAKING_PRECOMPILE,
1442-
args_offset=60,
1433+
args_offset=CORRECT_SEL_ARGS_OFFSET,
14431434
args_size=36,
14441435
ret_offset=0,
14451436
ret_size=32,

0 commit comments

Comments
 (0)