Skip to content

Commit

Permalink
new(tests): Explicit test for EXTDELEGATECALL value cost (#911)
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz authored Oct 28, 2024
1 parent 0f56ad4 commit 358a26f
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 22 deletions.
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)

0 comments on commit 358a26f

Please sign in to comment.