Skip to content
Closed
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
53 changes: 48 additions & 5 deletions src/ethereum/forks/amsterdam/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,13 @@
from .vm.gas import (
BLOB_SCHEDULE_MAX,
GAS_PER_BLOB,
STATE_BYTES_PER_NEW_ACCOUNT,
STATE_BYTES_PER_STORAGE_SET,
calculate_blob_gas_price,
calculate_data_fee,
calculate_excess_blob_gas,
calculate_total_blob_gas,
state_gas_per_byte,
)
from .vm.interpreter import MessageCallOutput, process_message_call

Expand Down Expand Up @@ -485,6 +488,8 @@ def check_transaction(
block_output: vm.BlockOutput,
tx: Transaction,
tx_state: TransactionState,
intrinsic_regular_gas: Uint,
intrinsic_state_gas: Uint,
) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
"""
Check if the transaction is includable in the block.
Expand All @@ -499,6 +504,10 @@ def check_transaction(
The transaction.
tx_state :
The transaction state tracker.
intrinsic_regular_gas :
The transaction's intrinsic regular gas.
intrinsic_state_gas :
The transaction's intrinsic state gas.

Returns
-------
Expand Down Expand Up @@ -546,16 +555,24 @@ def check_transaction(
is empty.

"""
# Regular gas is capped at TX_MAX_GAS_LIMIT per EIP-7825.
# State gas is not checked per-tx; block-end validation enforces
# max(block_regular_gas_used, block_state_gas_used) <= gas_limit.
# Both regular and state gas are validated before execution.
regular_gas_available = (
block_env.block_gas_limit - block_output.block_gas_used
)
state_gas_available = (
block_env.block_gas_limit - block_output.block_state_gas_used
)
blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used

if min(TX_MAX_GAS_LIMIT, tx.gas) > regular_gas_available:
raise GasUsedExceedsLimitError("regular gas used exceeds limit")
regular_gas_contribution = min(
TX_MAX_GAS_LIMIT, tx.gas - intrinsic_state_gas
)
if regular_gas_contribution > regular_gas_available:
raise GasUsedExceedsLimitError("regular gas exceeds limit")

state_gas_contribution = tx.gas - intrinsic_regular_gas
if state_gas_contribution > state_gas_available:
raise GasUsedExceedsLimitError("state gas exceeds limit")

tx_blob_gas_used = calculate_total_blob_gas(tx)
if tx_blob_gas_used > blob_gas_available:
Expand Down Expand Up @@ -988,6 +1005,8 @@ def process_transaction(
block_output=block_output,
tx=tx,
tx_state=tx_state,
intrinsic_regular_gas=intrinsic.regular,
intrinsic_state_gas=intrinsic.state,
)

sender_account = get_account(tx_state, sender)
Expand Down Expand Up @@ -1054,6 +1073,30 @@ def process_transaction(
tx_output.state_gas_left += tx_output.state_gas_used
tx_output.state_gas_used = Uint(0)

# Refund state gas for accounts created and destroyed in the
# same tx (EIP-6780). Covers account, storage, and code.
cost_per_state_byte = state_gas_per_byte(block_env.block_gas_limit)
for address in tx_output.accounts_to_delete:
if address in tx_state.created_accounts:
selfdestruct_refund = (
STATE_BYTES_PER_NEW_ACCOUNT * cost_per_state_byte
)
storage = tx_state.storage_writes.get(address, {})
created_slots = sum(1 for v in storage.values() if v != 0)
selfdestruct_refund += (
Uint(created_slots)
* STATE_BYTES_PER_STORAGE_SET
* cost_per_state_byte
)
account = get_account(tx_state, address)
code = get_code(tx_state, account.code_hash)
selfdestruct_refund += Uint(len(code)) * cost_per_state_byte
selfdestruct_refund = min(
selfdestruct_refund, tx_output.state_gas_used
)
tx_output.state_gas_left += selfdestruct_refund
tx_output.state_gas_used -= selfdestruct_refund

tx_gas_used_before_refund = (
tx.gas - tx_output.gas_left - tx_output.state_gas_left
)
Expand Down
9 changes: 4 additions & 5 deletions src/ethereum/forks/amsterdam/vm/eoa_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ def set_delegation(message: Message) -> None:
"""
Set the delegation code for the authorities in the message.

For existing accounts, adjusts intrinsic_state_gas downward since
no account creation is needed (only delegation code write).
For existing accounts, refunds the account-creation component of
state gas to the reservoir (no mutation of intrinsic_state_gas).

Parameters
----------
Expand Down Expand Up @@ -199,11 +199,10 @@ def set_delegation(message: Message) -> None:
continue

# For existing accounts, no account creation needed.
# Refund the account creation state gas to the reservoir
# and adjust intrinsic accounting to avoid double-counting.
# Refund the account creation state gas to the reservoir.
# intrinsic_state_gas is immutable after validation.
if account_exists(tx_state, authority):
refund = STATE_BYTES_PER_NEW_ACCOUNT * cost_per_state_byte
message.tx_env.intrinsic_state_gas -= refund
message.state_gas_reservoir += refund

if auth.address == NULL_ADDRESS:
Expand Down
Loading