Skip to content

Commit c46c46a

Browse files
authored
feat(tests): add call memory expansion with no return size test (#1952)
* add test * separate code gas measures * improve gas cost calculations * move test to another file * mark test valid from berlin onwards
1 parent 2c2c117 commit c46c46a

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

tests/frontier/opcodes/test_call.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""test `CALL` opcode."""
2+
3+
import pytest # type: ignore
4+
5+
from ethereum_test_forks import Fork
6+
from ethereum_test_tools import (
7+
Account,
8+
Alloc,
9+
Environment,
10+
StateTestFiller,
11+
Transaction,
12+
)
13+
from ethereum_test_tools.code.generators import CodeGasMeasure
14+
from ethereum_test_tools.vm.opcode import Opcodes as Op
15+
16+
17+
# TODO: There's an issue with gas definitions on forks previous to Berlin, remove this when fixed.
18+
# https://github.com/ethereum/execution-spec-tests/pull/1952#discussion_r2237634275
19+
@pytest.mark.valid_from("Berlin")
20+
def test_call_large_offset_mstore(
21+
state_test: StateTestFiller,
22+
pre: Alloc,
23+
fork: Fork,
24+
):
25+
"""
26+
CALL with ret_offset larger than memory size and ret_size zero
27+
Then do an MSTORE in that offset to see if memory was expanded in CALL.
28+
29+
This is for bug in a faulty EVM implementation where memory is expanded when it shouldn't.
30+
"""
31+
sender = pre.fund_eoa()
32+
33+
gsc = fork.gas_costs()
34+
mem_offset = 128 # arbitrary number
35+
36+
call_measure = CodeGasMeasure(
37+
code=Op.CALL(gas=0, ret_offset=mem_offset, ret_size=0),
38+
overhead_cost=gsc.G_VERY_LOW * len(Op.CALL.kwargs), # Cost of pushing CALL args
39+
extra_stack_items=1, # Because CALL pushes 1 item to the stack
40+
sstore_key=0,
41+
stop=False, # Because it's the first CodeGasMeasure
42+
)
43+
mstore_measure = CodeGasMeasure(
44+
code=Op.MSTORE(offset=mem_offset, value=1),
45+
overhead_cost=gsc.G_VERY_LOW * len(Op.MSTORE.kwargs), # Cost of pushing MSTORE args
46+
extra_stack_items=0,
47+
sstore_key=1,
48+
)
49+
50+
contract = pre.deploy_contract(call_measure + mstore_measure)
51+
52+
tx = Transaction(
53+
gas_limit=500_000,
54+
to=contract,
55+
value=0,
56+
sender=sender,
57+
)
58+
59+
# this call cost is just the address_access_cost
60+
call_cost = gsc.G_COLD_ACCOUNT_ACCESS
61+
62+
memory_expansion_gas_calc = fork.memory_expansion_gas_calculator()
63+
# mstore cost: base cost + expansion cost
64+
mstore_cost = gsc.G_MEMORY + memory_expansion_gas_calc(new_bytes=mem_offset + 1)
65+
state_test(
66+
env=Environment(),
67+
pre=pre,
68+
tx=tx,
69+
post={
70+
contract: Account(
71+
storage={
72+
0: call_cost,
73+
1: mstore_cost,
74+
},
75+
)
76+
},
77+
)

0 commit comments

Comments
 (0)