Skip to content

Commit 358a26f

Browse files
authored
new(tests): Explicit test for EXTDELEGATECALL value cost (#911)
1 parent 0f56ad4 commit 358a26f

File tree

2 files changed

+75
-22
lines changed

2 files changed

+75
-22
lines changed

tests/osaka/eip7692_eof_v1/eip7069_extcall/test_gas.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,47 @@ def test_ext_calls_gas(
140140
cold_gas=cold_gas + cost_memory_bytes(mem_expansion_bytes, 0),
141141
warm_gas=warm_gas + cost_memory_bytes(mem_expansion_bytes, 0),
142142
)
143+
144+
145+
@pytest.mark.parametrize("opcode", [Op.EXTCALL, Op.EXTDELEGATECALL, Op.EXTSTATICCALL])
146+
@pytest.mark.parametrize("value", [0, 1])
147+
def test_transfer_gas_is_cleared(
148+
state_test: StateTestFiller,
149+
pre: Alloc,
150+
state_env: Environment,
151+
opcode: Op,
152+
value: int,
153+
):
154+
"""
155+
Test that EXT*CALL call doesn't charge for value transfer, even if the outer call
156+
transfered value.
157+
158+
NOTE: This is particularly possible for EXTDELEGATECALL, which carries over the value sent
159+
in the outer call, however, we extend the test to all 3 EXT*CALL opcodes for good measure.
160+
"""
161+
noop_callee_address = pre.deploy_contract(Container.Code(Op.STOP))
162+
163+
extdelegatecall_contract_address = pre.deploy_contract(
164+
Container.Code(opcode(address=noop_callee_address) + Op.STOP)
165+
)
166+
167+
push_gas = (4 if opcode == Op.EXTCALL else 3) * 3
168+
169+
gas_test(
170+
state_test,
171+
state_env,
172+
pre,
173+
setup_code=Op.PUSH1(value) + Op.PUSH0 * 2 + Op.PUSH20(extdelegatecall_contract_address),
174+
subject_code=Op.EXTCALL,
175+
subject_balance=5 * value,
176+
tear_down_code=Op.STOP,
177+
# NOTE: CALL_WITH_VALUE_GAS is charged only once on the outer EXTCALL, while the base
178+
# call gas - twice.
179+
cold_gas=2 * COLD_ACCOUNT_ACCESS_GAS
180+
+ (CALL_WITH_VALUE_GAS if value > 0 else 0)
181+
+ push_gas,
182+
warm_gas=2 * WARM_ACCOUNT_ACCESS_GAS
183+
+ (CALL_WITH_VALUE_GAS if value > 0 else 0)
184+
+ push_gas,
185+
out_of_gas_testing=False,
186+
)

tests/osaka/eip7692_eof_v1/gas_test.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def gas_test(
3838
subject_address: Address | None = None,
3939
subject_balance: int = 0,
4040
oog_difference: int = 1,
41+
out_of_gas_testing: bool = True,
4142
):
4243
"""
4344
Creates a State Test to check the gas cost of a sequence of EOF code.
@@ -104,27 +105,33 @@ def gas_test(
104105
+ (Op.DUP3 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_warm_gas) + Op.SSTORE)
105106
# store cold gas: DUP2 is the gas of the baseline gas run
106107
+ (Op.DUP2 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_cold_gas) + Op.SSTORE)
107-
# oog gas run:
108-
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
109-
# - subtract the gas charged by the harness
110-
# - add warm gas charged by the subject
111-
# - subtract `oog_difference` to cause OOG exception (1 by default)
112-
+ Op.SSTORE(
113-
slot_oog_call_result,
114-
Op.CALL(
115-
gas=Op.ADD(warm_gas - gas_single_gas_run - oog_difference, Op.DUP7),
116-
address=address_subject,
117-
),
118-
)
119-
# sanity gas run: not subtracting 1 to see if enough gas makes the call succeed
120-
+ Op.SSTORE(
121-
slot_sanity_call_result,
122-
Op.CALL(
123-
gas=Op.ADD(warm_gas - gas_single_gas_run, Op.DUP7),
124-
address=address_subject,
125-
),
108+
+ (
109+
(
110+
# do an oog gas run, unless skipped with `out_of_gas_testing=False`:
111+
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
112+
# - subtract the gas charged by the harness
113+
# - add warm gas charged by the subject
114+
# - subtract `oog_difference` to cause OOG exception (1 by default)
115+
Op.SSTORE(
116+
slot_oog_call_result,
117+
Op.CALL(
118+
gas=Op.ADD(warm_gas - gas_single_gas_run - oog_difference, Op.DUP7),
119+
address=address_subject,
120+
),
121+
)
122+
# sanity gas run: not subtracting 1 to see if enough gas makes the call succeed
123+
+ Op.SSTORE(
124+
slot_sanity_call_result,
125+
Op.CALL(
126+
gas=Op.ADD(warm_gas - gas_single_gas_run, Op.DUP7),
127+
address=address_subject,
128+
),
129+
)
130+
+ Op.STOP
131+
)
132+
if out_of_gas_testing
133+
else Op.STOP
126134
)
127-
+ Op.STOP
128135
),
129136
evm_code_type=EVMCodeType.LEGACY, # Needs to be legacy to use GAS opcode
130137
)
@@ -134,12 +141,14 @@ def gas_test(
134141
storage={
135142
slot_warm_gas: warm_gas,
136143
slot_cold_gas: cold_gas,
137-
slot_oog_call_result: LEGACY_CALL_FAILURE,
138-
slot_sanity_call_result: LEGACY_CALL_SUCCESS,
139144
},
140145
),
141146
}
142147

148+
if out_of_gas_testing:
149+
post[address_legacy_harness].storage[slot_oog_call_result] = LEGACY_CALL_FAILURE
150+
post[address_legacy_harness].storage[slot_sanity_call_result] = LEGACY_CALL_SUCCESS
151+
143152
tx = Transaction(to=address_legacy_harness, gas_limit=env.gas_limit, sender=sender)
144153

145154
state_test(env=env, pre=pre, tx=tx, post=post)

0 commit comments

Comments
 (0)