Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions tests/osaka/eip7692_eof_v1/eip7069_extcall/test_gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,47 @@ def test_ext_calls_gas(
cold_gas=cold_gas + cost_memory_bytes(mem_expansion_bytes, 0),
warm_gas=warm_gas + cost_memory_bytes(mem_expansion_bytes, 0),
)


@pytest.mark.parametrize("opcode", [Op.EXTCALL, Op.EXTDELEGATECALL, Op.EXTSTATICCALL])
@pytest.mark.parametrize("value", [0, 1])
def test_transfer_gas_is_cleared(
state_test: StateTestFiller,
pre: Alloc,
state_env: Environment,
opcode: Op,
value: int,
):
"""
Test that EXT*CALL call doesn't charge for value transfer, even if the outer call
transfered value.

NOTE: This is particularly possible for EXTDELEGATECALL, which carries over the value sent
in the outer call, however, we extend the test to all 3 EXT*CALL opcodes for good measure.
"""
noop_callee_address = pre.deploy_contract(Container.Code(Op.STOP))

extdelegatecall_contract_address = pre.deploy_contract(
Container.Code(opcode(address=noop_callee_address) + Op.STOP)
)

push_gas = (4 if opcode == Op.EXTCALL else 3) * 3

gas_test(
state_test,
state_env,
pre,
setup_code=Op.PUSH1(value) + Op.PUSH0 * 2 + Op.PUSH20(extdelegatecall_contract_address),
subject_code=Op.EXTCALL,
subject_balance=5 * value,
tear_down_code=Op.STOP,
# NOTE: CALL_WITH_VALUE_GAS is charged only once on the outer EXTCALL, while the base
# call gas - twice.
cold_gas=2 * COLD_ACCOUNT_ACCESS_GAS
+ (CALL_WITH_VALUE_GAS if value > 0 else 0)
+ push_gas,
warm_gas=2 * WARM_ACCOUNT_ACCESS_GAS
+ (CALL_WITH_VALUE_GAS if value > 0 else 0)
+ push_gas,
out_of_gas_testing=False,
)
53 changes: 31 additions & 22 deletions tests/osaka/eip7692_eof_v1/gas_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def gas_test(
subject_address: Address | None = None,
subject_balance: int = 0,
oog_difference: int = 1,
out_of_gas_testing: bool = True,
):
"""
Creates a State Test to check the gas cost of a sequence of EOF code.
Expand Down Expand Up @@ -104,27 +105,33 @@ def gas_test(
+ (Op.DUP3 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_warm_gas) + Op.SSTORE)
# store cold gas: DUP2 is the gas of the baseline gas run
+ (Op.DUP2 + Op.SWAP1 + Op.SUB + Op.PUSH2(slot_cold_gas) + Op.SSTORE)
# oog gas run:
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
# - subtract the gas charged by the harness
# - add warm gas charged by the subject
# - subtract `oog_difference` to cause OOG exception (1 by default)
+ Op.SSTORE(
slot_oog_call_result,
Op.CALL(
gas=Op.ADD(warm_gas - gas_single_gas_run - oog_difference, Op.DUP7),
address=address_subject,
),
)
# sanity gas run: not subtracting 1 to see if enough gas makes the call succeed
+ Op.SSTORE(
slot_sanity_call_result,
Op.CALL(
gas=Op.ADD(warm_gas - gas_single_gas_run, Op.DUP7),
address=address_subject,
),
+ (
(
# do an oog gas run, unless skipped with `out_of_gas_testing=False`:
# - DUP7 is the gas of the baseline gas run, after other CALL args were pushed
# - subtract the gas charged by the harness
# - add warm gas charged by the subject
# - subtract `oog_difference` to cause OOG exception (1 by default)
Op.SSTORE(
slot_oog_call_result,
Op.CALL(
gas=Op.ADD(warm_gas - gas_single_gas_run - oog_difference, Op.DUP7),
address=address_subject,
),
)
# sanity gas run: not subtracting 1 to see if enough gas makes the call succeed
+ Op.SSTORE(
slot_sanity_call_result,
Op.CALL(
gas=Op.ADD(warm_gas - gas_single_gas_run, Op.DUP7),
address=address_subject,
),
)
+ Op.STOP
)
if out_of_gas_testing
else Op.STOP
)
+ Op.STOP
),
evm_code_type=EVMCodeType.LEGACY, # Needs to be legacy to use GAS opcode
)
Expand All @@ -134,12 +141,14 @@ def gas_test(
storage={
slot_warm_gas: warm_gas,
slot_cold_gas: cold_gas,
slot_oog_call_result: LEGACY_CALL_FAILURE,
slot_sanity_call_result: LEGACY_CALL_SUCCESS,
},
),
}

if out_of_gas_testing:
post[address_legacy_harness].storage[slot_oog_call_result] = LEGACY_CALL_FAILURE
post[address_legacy_harness].storage[slot_sanity_call_result] = LEGACY_CALL_SUCCESS

tx = Transaction(to=address_legacy_harness, gas_limit=env.gas_limit, sender=sender)

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