@pytest.mark.parametrize(
**gas_test_parameter_args(
include_pre_authorized=False, execution_gas_allowance=True
)
)
@pytest.mark.slow()
def test_gas_cost(
state_test: StateTestFiller,
pre: Alloc,
fork: Fork,
authorization_list_with_properties: List[AuthorizationWithProperties],
authorization_list: List[AuthorizationTuple],
data: bytes,
access_list: List[AccessList],
sender: EOA,
self_sponsored: bool,
) -> None:
"""
Test gas at the execution start of a set-code transaction in multiple
scenarios.
Pre-EIP-8037: intrinsic over-charges empty-account auth cost and refunds
existing authorities via ``refund_counter`` (EIP-3529 1/5 cap).
EIP-8037 / EIP-2780: intrinsic is execution-only; state-dependent auth
charges land at the top frame with no existing-authority refund. With
``gas_limit`` under the EIP-7825 cap the reservoir is empty, so state
charges spill into ``gas_left`` and ``GAS`` at code entry reports the
execution gas left after both top-frame charges.
"""
gas_opcode_cost = Op.GAS.gas_cost(fork)
sstore_opcode_count = 10
push_opcode_count = (2 * sstore_opcode_count) - 1
measurement_bytecode = (
Op.GAS
+ Op.PUSH1(0) * push_opcode_count
+ Op.SSTORE(key_warm=False) * sstore_opcode_count
)
header_gas_used: int | None = None
if fork.is_eip_enabled(8037):
annotated_auths = _annotate_authorizations_for_top_frame(
authorization_list_with_properties,
self_sponsored=self_sponsored,
sender=sender,
)
# Match ``allocate_evm_gas``: only the execution intrinsic is
# removed before the top frame; calldata floor is settled later.
intrinsic_execution = fork.transaction_intrinsic_cost_calculator()(
calldata=data,
access_list=access_list,
authorization_list_or_count=annotated_auths,
recipient_type=RecipientType.CONTRACT,
return_cost_deducted_prior_execution=True,
)
top_frame_execution = fork.transaction_top_frame_execution_gas(
recipient_type=RecipientType.CONTRACT,
authorizations=annotated_auths,
)
top_frame_state = fork.transaction_top_frame_state_gas(
recipient_type=RecipientType.CONTRACT,
authorizations=annotated_auths,
)
# Combined execution+state bytecode cost: with an empty reservoir
# the SSTORE state gas also spills into gas_left.
code_gas = measurement_bytecode.gas_cost(fork)
expected_gas_measure = code_gas - gas_opcode_cost
tx_gas_limit = (
intrinsic_execution
+ top_frame_execution
+ top_frame_state
+ code_gas
)
gas_limit_cap = fork.transaction_gas_limit_cap()
assert gas_limit_cap is None or tx_gas_limit <= gas_limit_cap
# The calldata floor never binds here: the per-authorization
# intrinsic charge alone exceeds the floor of the tiny calldata.
# If it did, the top frame would enter with the floor excess as
# extra gas_left and the GAS measure below would be wrong.
intrinsic_with_floor = fork.transaction_intrinsic_cost_calculator()(
calldata=data,
access_list=access_list,
authorization_list_or_count=annotated_auths,
recipient_type=RecipientType.CONTRACT,
)
assert tx_gas_limit >= intrinsic_with_floor
# No existing-authority refund.
gas_used = (
intrinsic_execution
+ top_frame_execution
+ top_frame_state
+ code_gas
)
header_gas_used = max(
intrinsic_execution
+ top_frame_execution
+ measurement_bytecode.execution_cost(fork),
top_frame_state + measurement_bytecode.state_cost(fork),
)
authorization_list = annotated_auths
else:
# Prague / Osaka: charge full empty-account auth cost in intrinsic,
# refund existing authorities via refund_counter (EIP-3529 cap).
intrinsic_gas = fork.transaction_intrinsic_cost_calculator()(
calldata=data,
access_list=access_list,
authorization_list_or_count=authorization_list,
)
discounted_authorizations = 0
seen_authority: set[Address] = set()
for (
authorization_with_properties
) in authorization_list_with_properties:
if authorization_with_properties.invalidity_type is None:
authority = authorization_with_properties.tuple.signer
assert authority is not None
if not authorization_with_properties.empty:
seen_authority.add(authority)
if authority in seen_authority:
discounted_authorizations += 1
else:
seen_authority.add(authority)
discount_gas = (
Spec.AUTH_PER_EMPTY_ACCOUNT - Spec.REFUND_AUTH_PER_EXISTING_ACCOUNT
) * discounted_authorizations
code_gas = measurement_bytecode.gas_cost(fork)
expected_gas_measure = code_gas - gas_opcode_cost
tx_gas_limit = intrinsic_gas + code_gas
# EIP-3529
max_discount = tx_gas_limit // 5
if discount_gas > max_discount:
# Only one test hits this condition, but it's ok to also test this
# case.
discount_gas = max_discount
gas_used = tx_gas_limit - discount_gas
test_code_storage = Storage()
test_code = (
Op.SSTORE(test_code_storage.store_next(expected_gas_measure), Op.GAS)
+ sum(
Op.SSTORE(test_code_storage.store_next(1), 1)
for _ in range(sstore_opcode_count - 1)
)
+ Op.STOP
)
test_code_address = pre.deploy_contract(test_code)
sender_account = pre[sender]
assert sender_account is not None
tx = Transaction(
gas_limit=tx_gas_limit,
to=test_code_address,
value=0,
data=data,
authorization_list=authorization_list,
access_list=access_list,
sender=sender,
expected_receipt=TransactionReceipt(cumulative_gas_used=gas_used),
)
state_test_kwargs: dict = {
"pre": pre,
"tx": tx,
"post": {
test_code_address: Account(storage=test_code_storage),
},
}
if header_gas_used is not None:
state_test_kwargs["blockchain_test_header_verify"] = Header(
gas_used=header_gas_used
)
state_test(**state_test_kwargs)