Skip to content

Commit c95b81c

Browse files
danceratopzmarioevzspencer-tb
authored
tests: add more transient storage/eip-1153 tests (#292)
* tests: add tstorage gas measure tests * tests: port simple tload follow sstore test from ethereum/tests * tests: add call to invalid/revert/oog exec contexts Also fix the arguments pushed for the delegatecall opcode; there was one too many. * tests/cancun/eip1153: small fix to storage pre * tests/cancun/eip1153: add self-destruct tests * tox: add 'selfdestructing' to whitelist.txt * style: fix incorrect comment * tests: fix xfail for revert by ensuring memory is clean * tests: add tstore in call then tload return in staticcall --------- Co-authored-by: Mario Vega <[email protected]> Co-authored-by: spencer-tb <[email protected]>
1 parent 0ef22d8 commit c95b81c

File tree

5 files changed

+490
-47
lines changed

5 files changed

+490
-47
lines changed

tests/cancun/eip1153_tstore/test_tstorage.py

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
[ethereum/tests/src/EIPTestsFiller/StateTests/stEIP1153-transientStorage/](https://github.com/ethereum/tests/blob/9b00b68593f5869eb51a6659e1cc983e875e616b/src/EIPTestsFiller/StateTests/stEIP1153-transientStorage)
77
""" # noqa: E501
88

9-
# from typing import Mapping
9+
from enum import unique
1010

1111
import pytest
1212

13-
from ethereum_test_tools import Account, Environment
13+
from ethereum_test_tools import Account, Code, CodeGasMeasure, Environment
1414
from ethereum_test_tools import Opcodes as Op
1515
from ethereum_test_tools import StateTestFiller, TestAddress, Transaction
1616

17-
from .spec import ref_spec_1153
17+
from . import PytestParameterEnum
18+
from .spec import Spec, ref_spec_1153
1819

1920
REFERENCE_SPEC_GIT_PATH = ref_spec_1153.git_path
2021
REFERENCE_SPEC_VERSION = ref_spec_1153.version
@@ -96,6 +97,52 @@ def test_tload_after_tstore(state_test: StateTestFiller):
9697
)
9798

9899

100+
def test_tload_after_sstore(state_test: StateTestFiller):
101+
"""
102+
Loading after storing returns the stored value: TSTORE(x, y), TLOAD(x)
103+
returns y.
104+
105+
Based on [ethereum/tests/.../18_tloadAfterStoreFiller.yml](https://github.com/ethereum/tests/blob/9b00b68593f5869eb51a6659e1cc983e875e616b/src/EIPTestsFiller/StateTests/stEIP1153-transientStorage/18_tloadAfterStoreFiller.yml)", # noqa: E501
106+
"""
107+
env = Environment()
108+
109+
slots_under_test = [1, 3, 2**128, 2**256 - 1]
110+
code = b"".join(
111+
[
112+
Op.SSTORE(slot - 1, 0xFF) + Op.SSTORE(slot, Op.TLOAD(slot - 1))
113+
for slot in slots_under_test
114+
]
115+
)
116+
117+
pre = {
118+
TestAddress: Account(balance=10_000_000),
119+
code_address: Account(code=code, storage={slot: 1 for slot in slots_under_test}),
120+
}
121+
122+
txs = [
123+
Transaction(
124+
to=code_address,
125+
data=b"",
126+
gas_limit=1_000_000,
127+
)
128+
]
129+
130+
post = {
131+
code_address: Account(
132+
code=code,
133+
storage={slot - 1: 0xFF for slot in slots_under_test}
134+
| {slot: 0 for slot in slots_under_test},
135+
)
136+
}
137+
138+
state_test(
139+
env=env,
140+
pre=pre,
141+
post=post,
142+
txs=txs,
143+
)
144+
145+
99146
def test_tload_after_tstore_is_zero(state_test: StateTestFiller):
100147
"""
101148
Test that tload returns zero after tstore is called with zero.
@@ -139,3 +186,73 @@ def test_tload_after_tstore_is_zero(state_test: StateTestFiller):
139186
post=post,
140187
txs=txs,
141188
)
189+
190+
191+
@unique
192+
class GasMeasureTestCases(PytestParameterEnum):
193+
"""
194+
Test cases for gas measurement.
195+
"""
196+
197+
TLOAD = {
198+
"description": "Test that tload() of an empty slot consumes the expected gas.",
199+
"bytecode": Op.TLOAD(10),
200+
"overhead_cost": 3, # 1 x PUSH1
201+
"extra_stack_items": 1,
202+
"expected_gas": Spec.TLOAD_GAS_COST,
203+
}
204+
TSTORE_TLOAD = {
205+
"description": "Test that tload() of a used slot consumes the expected gas.",
206+
"bytecode": Op.TSTORE(10, 10) + Op.TLOAD(10),
207+
"overhead_cost": 3 * 3, # 3 x PUSH1
208+
"extra_stack_items": 1,
209+
"expected_gas": Spec.TSTORE_GAS_COST + Spec.TLOAD_GAS_COST,
210+
}
211+
TSTORE_COLD = {
212+
"description": "Test that tstore() of a previously unused slot consumes the expected gas.",
213+
"bytecode": Op.TSTORE(10, 10),
214+
"overhead_cost": 2 * 3, # 2 x PUSH1
215+
"extra_stack_items": 0,
216+
"expected_gas": Spec.TSTORE_GAS_COST,
217+
}
218+
TSTORE_WARM = {
219+
"description": "Test that tstore() of a previously used slot consumes the expected gas.",
220+
"bytecode": Op.TSTORE(10, 10) + Op.TSTORE(10, 11),
221+
"overhead_cost": 4 * 3, # 4 x PUSH1
222+
"extra_stack_items": 0,
223+
"expected_gas": 2 * Spec.TSTORE_GAS_COST,
224+
}
225+
226+
227+
@GasMeasureTestCases.parametrize()
228+
def test_gas_usage(
229+
state_test: StateTestFiller,
230+
bytecode: Code,
231+
expected_gas: int,
232+
overhead_cost: int,
233+
extra_stack_items: int,
234+
):
235+
"""
236+
Test that tstore and tload consume the expected gas.
237+
"""
238+
gas_measure_bytecode = CodeGasMeasure(
239+
code=bytecode, overhead_cost=overhead_cost, extra_stack_items=extra_stack_items
240+
)
241+
242+
env = Environment()
243+
pre = {
244+
TestAddress: Account(balance=10_000_000, nonce=0),
245+
code_address: Account(code=gas_measure_bytecode),
246+
}
247+
txs = [
248+
Transaction(
249+
to=code_address,
250+
data=b"",
251+
gas_limit=1_000_000,
252+
)
253+
]
254+
post = {
255+
code_address: Account(code=gas_measure_bytecode, storage={0: expected_gas}),
256+
TestAddress: Account(nonce=1),
257+
}
258+
state_test(env=env, pre=pre, txs=txs, post=post)

tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,20 @@ class DynamicCallContextTestCases(EnumMeta):
3939

4040
def __new__(cls, name, bases, classdict): # noqa: D102
4141
for opcode in [Op.CALLCODE, Op.DELEGATECALL]:
42+
if opcode == Op.DELEGATECALL:
43+
contract_call = opcode(Op.GAS(), callee_address, 0, 0, 0, 0)
44+
elif opcode == Op.CALLCODE:
45+
contract_call = opcode(Op.GAS(), callee_address, 0, 0, 0, 0, 0)
46+
else:
47+
raise ValueError("Unexpected opcode.")
4248
classdict[opcode._name_] = {
4349
"description": (
4450
"Caller and callee contracts share transient storage when callee is "
4551
f"called via {opcode._name_}."
4652
),
4753
"caller_bytecode": (
4854
Op.TSTORE(0, 420)
49-
+ Op.SSTORE(0, opcode(Op.GAS(), callee_address, 0, 0, 0, 0, 0))
55+
+ Op.SSTORE(0, contract_call)
5056
+ Op.SSTORE(1, Op.TLOAD(0))
5157
+ Op.SSTORE(4, Op.TLOAD(1))
5258
),
@@ -56,50 +62,69 @@ def __new__(cls, name, bases, classdict): # noqa: D102
5662
"expected_caller_storage": {0: 1, 1: 420, 2: 420, 3: 69, 4: 69},
5763
"expected_callee_storage": {},
5864
}
65+
66+
for opcode in [Op.CALL, Op.CALLCODE, Op.DELEGATECALL]:
67+
if opcode == Op.DELEGATECALL:
68+
contract_call = opcode(Op.GAS(), callee_address, 0, 0, 0, 0)
69+
elif opcode in [Op.CALL, Op.CALLCODE]:
70+
contract_call = opcode(Op.GAS(), callee_address, 0, 0, 0, 0, 0)
71+
else:
72+
raise ValueError("Unexpected opcode.")
5973
classdict[f"{opcode._name_}_WITH_REVERT"] = {
6074
"description": (
61-
"Caller and callee contracts share transient storage when callee is "
62-
f"called via {opcode._name_} but transient storage usage is discarded "
63-
"from sub-call upon REVERT."
75+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
76+
"upon REVERT."
6477
),
6578
"caller_bytecode": (
6679
Op.TSTORE(0, 420)
6780
+ Op.TSTORE(1, 420)
68-
+ Op.SSTORE(0, opcode(Op.GAS(), callee_address, 0, 0, 0, 0, 0))
81+
+ Op.SSTORE(0, contract_call)
6982
+ Op.SSTORE(1, Op.TLOAD(0))
7083
+ Op.SSTORE(2, Op.TLOAD(1))
7184
),
7285
"callee_bytecode": Op.TSTORE(1, 69) + Op.REVERT(0, 0),
7386
"expected_caller_storage": {0: 0, 1: 420, 2: 420},
7487
"expected_callee_storage": {},
7588
}
89+
90+
if opcode == Op.DELEGATECALL:
91+
contract_call = opcode(0xFF, callee_address, 0, 0, 0, 0)
92+
elif opcode in [Op.CALL, Op.CALLCODE]:
93+
contract_call = opcode(0xFF, callee_address, 0, 0, 0, 0, 0)
94+
else:
95+
raise ValueError("Unexpected opcode.")
7696
classdict[f"{opcode._name_}_WITH_INVALID"] = {
7797
"description": (
78-
"Caller and callee contracts share transient storage when callee is "
79-
f"called via {opcode._name_} but transient storage usage is discarded "
80-
"from sub-call upon INVALID. Note: Gas passed to sub-call is capped."
98+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
99+
"upon REVERT. Note: Gas passed to sub-call is capped."
81100
),
82101
"caller_bytecode": (
83102
Op.TSTORE(0, 420)
84103
+ Op.TSTORE(1, 420)
85-
+ Op.SSTORE(0, opcode(0xFF, callee_address, 0, 0, 0, 0, 0))
104+
+ Op.SSTORE(0, contract_call)
86105
+ Op.SSTORE(1, Op.TLOAD(0))
87106
+ Op.SSTORE(2, Op.TLOAD(1))
88107
),
89108
"callee_bytecode": Op.TSTORE(1, 69) + Op.INVALID(),
90109
"expected_caller_storage": {0: 0, 1: 420, 2: 420},
91110
"expected_callee_storage": {},
92111
}
112+
113+
if opcode == Op.DELEGATECALL:
114+
contract_call = opcode(0xFFFF, callee_address, 0, 0, 0, 0)
115+
elif opcode in [Op.CALL, Op.CALLCODE]:
116+
contract_call = opcode(0xFFFF, callee_address, 0, 0, 0, 0, 0)
117+
else:
118+
raise ValueError("Unexpected opcode.")
93119
classdict[f"{opcode._name_}_WITH_STACK_OVERFLOW"] = {
94120
"description": (
95-
"Caller and callee contracts share transient storage when callee is "
96-
f"called via {opcode._name_} but transient storage usage is discarded "
97-
"from sub-call upon stack overflow."
121+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
122+
"upon stack overflow."
98123
),
99124
"caller_bytecode": (
100125
Op.TSTORE(0, 420)
101126
+ Op.TSTORE(1, 420)
102-
+ Op.SSTORE(0, opcode(0xFFFF, callee_address, 0, 0, 0, 0, 0))
127+
+ Op.SSTORE(0, contract_call)
103128
+ Op.SSTORE(1, Op.TLOAD(0))
104129
+ Op.SSTORE(2, Op.TLOAD(1))
105130
),
@@ -109,14 +134,13 @@ def __new__(cls, name, bases, classdict): # noqa: D102
109134
}
110135
classdict[f"{opcode._name_}_WITH_TSTORE_STACK_UNDERFLOW"] = {
111136
"description": (
112-
"Caller and callee contracts share transient storage when callee is "
113-
f"called via {opcode._name_} but transient storage usage is discarded "
114-
"from sub-call upon stack underflow because of TSTORE parameters (1)."
137+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
138+
"upon stack underflow because of TSTORE parameters (1)."
115139
),
116140
"caller_bytecode": (
117141
Op.TSTORE(0, 420)
118142
+ Op.TSTORE(1, 420)
119-
+ Op.SSTORE(0, opcode(0xFFFF, callee_address, 0, 0, 0, 0, 0))
143+
+ Op.SSTORE(0, contract_call)
120144
+ Op.SSTORE(1, Op.TLOAD(0))
121145
+ Op.SSTORE(2, Op.TLOAD(1))
122146
),
@@ -126,14 +150,13 @@ def __new__(cls, name, bases, classdict): # noqa: D102
126150
}
127151
classdict[f"{opcode._name_}_WITH_TSTORE_STACK_UNDERFLOW_2"] = {
128152
"description": (
129-
"Caller and callee contracts share transient storage when callee is "
130-
f"called via {opcode._name_} but transient storage usage is discarded "
131-
"from sub-call upon stack underflow because of TSTORE parameters (0)."
153+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
154+
" upon stack underflow because of TSTORE parameters (0)."
132155
),
133156
"caller_bytecode": (
134157
Op.TSTORE(0, 420)
135158
+ Op.TSTORE(1, 420)
136-
+ Op.SSTORE(0, opcode(0xFFFF, callee_address, 0, 0, 0, 0, 0))
159+
+ Op.SSTORE(0, contract_call)
137160
+ Op.SSTORE(1, Op.TLOAD(0))
138161
+ Op.SSTORE(2, Op.TLOAD(1))
139162
),
@@ -143,27 +166,36 @@ def __new__(cls, name, bases, classdict): # noqa: D102
143166
}
144167
classdict[f"{opcode._name_}_WITH_TLOAD_STACK_UNDERFLOW"] = {
145168
"description": (
146-
"Caller and callee contracts share transient storage when callee is "
147-
f"called via {opcode._name_} but transient storage usage is discarded "
148-
"from sub-call upon stack underflow because of TLOAD parameters (0)."
169+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
170+
"upon stack underflow because of TLOAD parameters (0)."
149171
),
150172
"caller_bytecode": (
151173
Op.TSTORE(0, 420)
152174
+ Op.TSTORE(1, 420)
153-
+ Op.SSTORE(0, opcode(0xFFFF, callee_address, 0, 0, 0, 0, 0))
175+
+ Op.SSTORE(0, contract_call)
154176
+ Op.SSTORE(1, Op.TLOAD(0))
155177
+ Op.SSTORE(2, Op.TLOAD(1))
156178
),
157179
"callee_bytecode": Op.TLOAD + Op.TSTORE(0, 1),
158180
"expected_caller_storage": {0: 0, 1: 420, 2: 420},
159181
"expected_callee_storage": {},
160182
}
183+
184+
gas_limit = Spec.TSTORE_GAS_COST + (PUSH_OPCODE_COST * 2) - 1
185+
if opcode == Op.DELEGATECALL:
186+
contract_call = opcode(
187+
opcode(gas_limit, callee_address, 0, 0, 0, 0),
188+
)
189+
elif opcode in [Op.CALL, Op.CALLCODE]:
190+
contract_call = opcode(
191+
opcode(gas_limit, callee_address, 0, 0, 0, 0, 0),
192+
)
193+
else:
194+
raise ValueError("Unexpected opcode.")
161195
classdict[f"{opcode._name_}_WITH_OUT_OF_GAS"] = {
162196
"description": (
163-
"Caller and callee contracts share transient storage when callee is "
164-
f"called via {opcode._name_} but transient storage usage is discarded "
165-
"from sub-call upon out of gas during TSTORE. Note: Gas passed to sub-call is "
166-
"capped."
197+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
198+
"upon out of gas during TSTORE. Note: Gas passed to sub-call is capped."
167199
),
168200
"caller_bytecode": (
169201
Op.TSTORE(0, 420)
@@ -187,17 +219,22 @@ def __new__(cls, name, bases, classdict): # noqa: D102
187219
"expected_caller_storage": {0: 0, 1: 420, 2: 420},
188220
"expected_callee_storage": {},
189221
}
222+
223+
if opcode == Op.DELEGATECALL:
224+
contract_call = opcode(0xFF, callee_address, 0, 0, 0, 0)
225+
elif opcode in [Op.CALL, Op.CALLCODE]:
226+
contract_call = opcode(0xFF, callee_address, 0, 0, 0, 0, 0)
227+
else:
228+
raise ValueError("Unexpected opcode.")
190229
classdict[f"{opcode._name_}_WITH_OUT_OF_GAS_2"] = {
191230
"description": (
192-
"Caller and callee contracts share transient storage when callee is "
193-
f"called via {opcode._name_} but transient storage usage is discarded "
194-
"from sub-call upon out of gas after TSTORE. Note: Gas passed to sub-call is "
195-
"capped."
231+
f"Transient storage usage is discarded from sub-call with {opcode._name_} "
232+
"upon out of gas after TSTORE. Note: Gas passed to sub-call is capped."
196233
),
197234
"caller_bytecode": (
198235
Op.TSTORE(0, 420)
199236
+ Op.TSTORE(1, 420)
200-
+ Op.SSTORE(0, opcode(0xFF, callee_address, 0, 0, 0, 0, 0))
237+
+ Op.SSTORE(0, contract_call)
201238
+ Op.SSTORE(1, Op.TLOAD(0))
202239
+ Op.SSTORE(2, Op.TLOAD(1))
203240
),
@@ -264,7 +301,7 @@ class CallContextTestCases(PytestParameterEnum, metaclass=DynamicCallContextTest
264301
+ Op.SSTORE(0, Op.STATICCALL(Op.GAS(), callee_address, 0, 0, 0, 0))
265302
+ Op.SSTORE(1, Op.TLOAD(0))
266303
),
267-
"callee_bytecode": Op.TLOAD(0), # calling tload does fail the call
304+
"callee_bytecode": Op.TLOAD(0), # calling tload does not cause the call to fail
268305
"expected_caller_storage": {0: 1, 1: 420},
269306
"expected_callee_storage": {},
270307
}

0 commit comments

Comments
 (0)