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
24 changes: 24 additions & 0 deletions docs/writing_tests/test_markers.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,30 @@ def test_something_with_all_system_contracts(

In this example, the test will be parameterized for parameter `system_contract` with value `[0x000F3DF6D732807EF1319FB7B8BB8522D0BEAC02]` for fork Cancun.

### `@pytest.mark.with_all_refund_types`

This marker is used to automatically parameterize a test with all types of refunds that are valid for the fork being tested.

Useful to mark tests to fail if a new refund type is introduced by a future fork and the test needs to be kept up to date and maintained.

```python
import pytest

from execution_testing import Address, Alloc, RefundTypes, StateTestFiller

@pytest.mark.with_all_refund_types
@pytest.mark.valid_from("Prague")
def test_something_with_all_refund_types(
state_test: StateTestFiller,
pre: Alloc,
refund_type: RefundTypes,
):
pass

```

In this example, the test will be parameterized for parameter `refund_type` with value `[RefundTypes.STORAGE_CLEAR, RefundTypes.AUTHORIZATION_EXISTING_AUTHORITY]` for fork Prague.

### Covariant Marker Keyword Arguments

All fork covariant markers accept the following keyword arguments:
Expand Down
3 changes: 2 additions & 1 deletion packages/testing/src/execution_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
TransactionException,
)
from .fixtures import BaseFixture, FixtureCollector
from .forks import Fork, GasCosts, TransitionFork
from .forks import Fork, GasCosts, RefundTypes, TransitionFork
from .specs import (
BaseTest,
BenchmarkTest,
Expand Down Expand Up @@ -185,6 +185,7 @@
"ParameterSet",
"ReferenceSpec",
"ReferenceSpecTypes",
"RefundTypes",
"Removable",
"Requests",
"SequentialAddressLayout",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,15 @@ def covariant_decorator(
fork_attribute_name="system_contracts",
argnames=["system_contract"],
),
covariant_decorator(
marker_name="with_all_refund_types",
description=(
"marks a test to be parametrized for all refund types at "
"parameter named refund_type"
),
fork_attribute_name="refund_types",
argnames=["refund_type"],
),
]


Expand Down
2 changes: 2 additions & 0 deletions packages/testing/src/execution_testing/forks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Ethereum test fork definitions."""

from .base_fork import RefundTypes
from .forks.forks import (
BPO1,
BPO2,
Expand Down Expand Up @@ -87,6 +88,7 @@
"TransitionFork",
"TransitionForkAdapter",
"TransitionForkOrNoneAdapter",
"RefundTypes",
"Amsterdam",
"ArrowGlacier",
"Berlin",
Expand Down
17 changes: 17 additions & 0 deletions packages/testing/src/execution_testing/forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import re
from abc import ABCMeta, abstractmethod
from enum import Enum, auto
from typing import (
TYPE_CHECKING,
Callable,
Expand Down Expand Up @@ -168,6 +169,13 @@ def __call__(
pass


class RefundTypes(Enum):
"""Enum used to describe all refund types a fork can have."""

STORAGE_CLEAR = auto()
AUTHORIZATION_EXISTING_AUTHORITY = auto()


class BaseForkMeta(ABCMeta):
"""Metaclass for BaseFork."""

Expand Down Expand Up @@ -979,6 +987,15 @@ def max_request_type(cls) -> int:
"""Return max request type supported by the fork."""
pass

@classmethod
@abstractmethod
def refund_types(cls) -> List[RefundTypes]:
"""
Return the list of refund types that are possible given current
fork logic.
"""
pass

# Meta information about the fork
@classmethod
def name(cls) -> str:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
EIP-7778: Block Gas Accounting without Refunds.
Prevent Block Gas Limit Circumvention by Excluding Refunds from Block Gas
Accounting.
https://eips.ethereum.org/EIPS/eip-7778
"""

from ....base_fork import BaseFork


class EIP7778(BaseFork):
"""EIP-7778 class."""

pass
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from ....base_fork import (
BaseFork,
RefundTypes,
TransactionIntrinsicCostCalculator,
)
from ....gas_costs import GasCosts
Expand Down Expand Up @@ -95,3 +96,12 @@ def fn(
return intrinsic_cost

return fn

@classmethod
def refund_types(cls) -> List[RefundTypes]:
"""
At Prague, existing authorization refund is introduced.
"""
refunds = super(EIP7702, cls).refund_types()
refunds.append(RefundTypes.AUTHORIZATION_EXISTING_AUTHORITY)
return refunds
8 changes: 8 additions & 0 deletions packages/testing/src/execution_testing/forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
CalldataGasCalculator,
ExcessBlobGasCalculator,
MemoryExpansionGasCalculator,
RefundTypes,
TransactionDataFloorCostCalculator,
TransactionIntrinsicCostCalculator,
)
Expand Down Expand Up @@ -1199,6 +1200,13 @@ def max_request_type(cls) -> int:
"""At genesis, no request type is supported, signaled by -1."""
return -1

@classmethod
def refund_types(cls) -> List[RefundTypes]:
"""
At genesis, storage clearing refund is introduced.
"""
return [RefundTypes.STORAGE_CLEAR]

@classmethod
def pre_allocation(cls) -> Mapping:
"""
Expand Down
10 changes: 10 additions & 0 deletions packages/testing/src/execution_testing/specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ class Block(Header):
"""Post state for verification after block execution in BlockchainTest"""
block_access_list: Bytes | None = Field(None)
"""EIP-7928: Block-level access lists (serialized)."""
expected_gas_used: int | None = None
"""Expected gas used for the block."""

def set_environment(self, env: Environment) -> Environment:
"""
Expand Down Expand Up @@ -695,6 +697,14 @@ def generate_block_data(
f"Verification of block {int(env.number)} failed"
) from e

if block.expected_gas_used is not None:
gas_used = int(transition_tool_output.result.gas_used)
assert gas_used == block.expected_gas_used, (
f"gas_used ({gas_used}) does not match expected_gas_used "
f"({block.expected_gas_used})"
f", difference: {gas_used - block.expected_gas_used}"
)

requests_list: List[Bytes] | None = None
if fork.header_requests_required():
assert transition_tool_output.result.requests is not None, (
Expand Down
1 change: 1 addition & 0 deletions src/ethereum/forks/amsterdam/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ class Receipt:
cumulative_gas_used: Uint
"""
Total gas used in the block up to and including this transaction.
This is the gas used after refunds, paid by the user.
"""

bloom: Bloom
Expand Down
19 changes: 12 additions & 7 deletions src/ethereum/forks/amsterdam/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ def make_receipt(
Error in the top level frame of the transaction, if any.
cumulative_gas_used :
The total gas used so far in the block after the transaction was
executed.
executed. This is the gas used after refunds.
logs :
The logs produced by the transaction.

Expand Down Expand Up @@ -1039,16 +1039,17 @@ def process_transaction(

# Transactions with less execution_gas_used than the floor pay at the
# floor cost.
tx_gas_used_after_refund = max(
tx_gas_used_after_refund, calldata_floor_gas_cost
tx_gas_used = max(tx_gas_used_after_refund, calldata_floor_gas_cost)
block_gas_used_in_tx = max(
tx_gas_used_before_refund, calldata_floor_gas_cost
)

tx_gas_left = tx.gas - tx_gas_used_after_refund
tx_gas_left = tx.gas - tx_gas_used
gas_refund_amount = tx_gas_left * effective_gas_price

# For non-1559 transactions effective_gas_price == tx.gas_price
priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
transaction_fee = tx_gas_used * priority_fee_per_gas

# refund gas
sender_balance_after_refund = get_account(tx_state, sender).balance + U256(
Expand Down Expand Up @@ -1090,11 +1091,15 @@ def process_transaction(
):
destroy_account(tx_state, block_env.coinbase)

block_output.block_gas_used += tx_gas_used_after_refund
block_output.cumulative_gas_used += tx_gas_used
block_output.block_gas_used += block_gas_used_in_tx
block_output.blob_gas_used += tx_blob_gas_used

receipt = make_receipt(
tx, tx_output.error, block_output.block_gas_used, all_logs
tx,
tx_output.error,
block_output.cumulative_gas_used,
all_logs,
)

receipt_key = rlp.encode(Uint(index))
Expand Down
1 change: 1 addition & 0 deletions src/ethereum/forks/amsterdam/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class BlockOutput:
"""

block_gas_used: Uint = Uint(0)
cumulative_gas_used: Uint = Uint(0)
transactions_trie: Trie[Bytes, Optional[Bytes | LegacyTransaction]] = (
field(default_factory=lambda: Trie(secured=False, default=None))
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for [EIP-7778: Block Gas Accounting without Refunds](https://eips.ethereum.org/EIPS/eip-7778)."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
general/code_coverage/eels = Please check https://app.codecov.io/gh/ethereum/execution-specs/pull/2840 for relevant test coverage
general/code_coverage/test_coverage = Please run the test with `--cov` flag for final coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
opcode = EIP does not introduce a new opcode
precompile = EIP does not introduce a new precompile
removed_precompile = EIP does not remove a precompile
system_contract = EIP does not introduce a new system contract
transaction_type = EIP does not introduce a new transaction type
block_header_field = EIP does not add any new block header fields
block_body_field = EIP does not add any new block body fields
gas_cost_changes = EIP does not modify per-operation gas costs
gas_refunds_changes/test/refund_calculation/over = EIP does not change refund calculation, only block-level accounting
gas_refunds_changes/test/refund_calculation/exact = EIP does not change refund calculation, only block-level accounting
gas_refunds_changes/test/refund_calculation/under = EIP does not change refund calculation, only block-level accounting
gas_refunds_changes/test/exceptional_abort/revertable = EIP does not change refund behavior on revertable aborts
gas_refunds_changes/test/exceptional_abort/non_revertable = EIP does not change refund behavior on non-revertable aborts
blob_count_changes = EIP does not introduce any blob count changes
execution_layer_request = EIP does not introduce an execution layer request
new_transaction_validity_constraint = EIP does not introduce a new transaction validity constraint
modified_transaction_validity_constraint = EIP does not introduce a modified transaction validity constraint
Loading
Loading