Skip to content

feat(tests): eip-7918 blob base fee bounded by execution cost #1685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Users can select any of the artifacts depending on their testing needs for their
- 🔀 Refactored EIP-145 static tests into python ([#1683](https://github.com/ethereum/execution-spec-tests/pull/1683)).
- ✨ EIP-7823, EIP-7883: Add test cases for ModExp precompile gas-cost updates and input limits on Osaka ([#1579](https://github.com/ethereum/execution-spec-tests/pull/1579)).
- ✨ [EIP-7825](https://eips.ethereum.org/EIPS/eip-7825): Add test cases for the transaction gas limit of 30M gas ([#1711](https://github.com/ethereum/execution-spec-tests/pull/1711)).
- ✨ [EIP-7918](https://eips.ethereum.org/EIPS/eip-7918): Blob base fee bounded by execution cost test cases (initial), includes some adjustments to [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) tests ([#1685](https://github.com/ethereum/execution-spec-tests/pull/1685)).

## [v4.5.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v4.5.0) - 2025-05-14

Expand Down
1 change: 1 addition & 0 deletions src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def __call__(
parent_excess_blobs: int | None = None,
parent_blob_gas_used: int | None = None,
parent_blob_count: int | None = None,
parent_base_fee_per_gas: int,
) -> int:
"""Return the excess blob gas given the parent's excess blob gas and blob gas used."""
pass
Expand Down
50 changes: 50 additions & 0 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ def fn(
parent_excess_blobs: int | None = None,
parent_blob_gas_used: int | None = None,
parent_blob_count: int | None = None,
parent_base_fee_per_gas: int, # Required for Osaka as using this as base
) -> int:
if parent_excess_blob_gas is None:
assert parent_excess_blobs is not None, "Parent excess blobs are required"
Expand Down Expand Up @@ -1362,6 +1363,55 @@ def solc_min_version(cls) -> Version:
"""Return minimum version of solc that supports this fork."""
return Version.parse("1.0.0") # set a high version; currently unknown

@classmethod
def excess_blob_gas_calculator(
cls, block_number: int = 0, timestamp: int = 0
) -> ExcessBlobGasCalculator:
"""Return a callable that calculates the excess blob gas for a block."""
target_blobs_per_block = cls.target_blobs_per_block(block_number, timestamp)
blob_gas_per_blob = cls.blob_gas_per_blob(block_number, timestamp)
target_blob_gas_per_block = target_blobs_per_block * blob_gas_per_blob
max_blobs_per_block = cls.max_blobs_per_block(block_number, timestamp)
blob_base_cost = 2**14 # EIP-7918 new parameter

def fn(
*,
parent_excess_blob_gas: int | None = None,
parent_excess_blobs: int | None = None,
parent_blob_gas_used: int | None = None,
parent_blob_count: int | None = None,
parent_base_fee_per_gas: int, # EIP-7918 additional parameter
) -> int:
if parent_excess_blob_gas is None:
assert parent_excess_blobs is not None, "Parent excess blobs are required"
parent_excess_blob_gas = parent_excess_blobs * blob_gas_per_blob
if parent_blob_gas_used is None:
assert parent_blob_count is not None, "Parent blob count is required"
parent_blob_gas_used = parent_blob_count * blob_gas_per_blob
if parent_excess_blob_gas + parent_blob_gas_used < target_blob_gas_per_block:
return 0

# EIP-7918: Apply reserve price when execution costs dominate blob costs
current_blob_base_fee = cls.blob_gas_price_calculator()(
excess_blob_gas=parent_excess_blob_gas
)
reserve_price_active = (
blob_base_cost * parent_base_fee_per_gas
> blob_gas_per_blob * current_blob_base_fee
)
if reserve_price_active:
blob_excess_adjustment = (
parent_blob_gas_used
* (max_blobs_per_block - target_blobs_per_block)
// max_blobs_per_block
)
return parent_excess_blob_gas + blob_excess_adjustment

# Original EIP-4844 calculation
return parent_excess_blob_gas + parent_blob_gas_used - target_blob_gas_per_block

return fn


class EOFv1(Prague, solc_name="cancun"):
"""EOF fork."""
Expand Down
26 changes: 18 additions & 8 deletions tests/cancun/eip4844_blobs/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def excess_blob_gas(
fork: Fork,
parent_excess_blobs: int | None,
parent_blobs: int | None,
block_base_fee_per_gas: int,
) -> int | None:
"""
Calculate the excess blob gas of the block under test from the parent block.
Expand All @@ -78,10 +79,10 @@ def excess_blob_gas(
"""
if parent_excess_blobs is None or parent_blobs is None:
return None
excess_blob_gas = fork.excess_blob_gas_calculator()
return excess_blob_gas(
return fork.excess_blob_gas_calculator()(
parent_excess_blobs=parent_excess_blobs,
parent_blob_count=parent_blobs,
parent_base_fee_per_gas=block_base_fee_per_gas,
)


Expand All @@ -90,6 +91,7 @@ def correct_excess_blob_gas(
fork: Fork,
parent_excess_blobs: int | None,
parent_blobs: int | None,
block_base_fee_per_gas: int,
) -> int:
"""
Calculate the correct excess blob gas of the block under test from the parent block.
Expand All @@ -98,18 +100,19 @@ def correct_excess_blob_gas(
"""
if parent_excess_blobs is None or parent_blobs is None:
return 0
excess_blob_gas = fork.excess_blob_gas_calculator()
return excess_blob_gas(
return fork.excess_blob_gas_calculator()(
parent_excess_blobs=parent_excess_blobs,
parent_blob_count=parent_blobs,
parent_base_fee_per_gas=block_base_fee_per_gas,
)


@pytest.fixture
def block_fee_per_blob_gas( # noqa: D103
def block_fee_per_blob_gas(
fork: Fork,
correct_excess_blob_gas: int,
) -> int:
"""Calculate the blob gas price for the current block."""
get_blob_gas_price = fork.blob_gas_price_calculator()
return get_blob_gas_price(excess_blob_gas=correct_excess_blob_gas)

Expand Down Expand Up @@ -251,7 +254,7 @@ def non_zero_blob_gas_used_genesis_block(
genesis_excess_blob_gas: int,
parent_excess_blob_gas: int,
tx_max_fee_per_gas: int,
target_blobs_per_block: int,
block_base_fee_per_gas: int,
) -> Block | None:
"""
For test cases with a non-zero blobGasUsed field in the
Expand All @@ -271,10 +274,17 @@ def non_zero_blob_gas_used_genesis_block(
return None

excess_blob_gas_calculator = fork.excess_blob_gas_calculator(block_number=1)
assert parent_excess_blob_gas == excess_blob_gas_calculator(
calculated_excess_blob_gas = excess_blob_gas_calculator(
parent_excess_blob_gas=genesis_excess_blob_gas,
parent_blob_count=0,
), "parent excess blob gas is not as expected for extra block"
parent_base_fee_per_gas=block_base_fee_per_gas,
)

assert parent_excess_blob_gas == calculated_excess_blob_gas, (
f"parent excess blob gas mismatch: expected {parent_excess_blob_gas}, "
f"got {calculated_excess_blob_gas} for {parent_blobs} blobs "
f"with base_fee_per_gas {block_base_fee_per_gas}"
)

sender = pre.fund_eoa(10**27)

Expand Down
1 change: 1 addition & 0 deletions tests/cancun/eip4844_blobs/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Spec:
# LIMIT_BLOBS_PER_TX = 2**12
HASH_OPCODE_BYTE = 0x49
HASH_GAS_COST = 3
GAS_PER_BLOB = 2**17

@classmethod
def kzg_to_versioned_hash(
Expand Down
4 changes: 4 additions & 0 deletions tests/cancun/eip4844_blobs/test_blob_txs.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ def expected_excess_blob_gas(
parent_blobs: Optional[int],
block_number: int,
block_timestamp: int,
block_base_fee_per_gas: int,
) -> Optional[int | Removable]:
"""Calculate blob gas used by the test block."""
if not fork.header_excess_blob_gas_required(
Expand All @@ -322,6 +323,7 @@ def expected_excess_blob_gas(
return excess_blob_gas(
parent_excess_blobs=parent_excess_blobs if parent_excess_blobs else 0,
parent_blob_count=parent_blobs if parent_blobs else 0,
parent_base_fee_per_gas=block_base_fee_per_gas,
)


Expand Down Expand Up @@ -374,6 +376,7 @@ def block(
"blobs_per_tx",
SpecHelpers.all_valid_blob_combinations,
)
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 100])
@pytest.mark.valid_from("Cancun")
def test_valid_blob_tx_combinations(
blockchain_test: BlockchainTestFiller,
Expand Down Expand Up @@ -664,6 +667,7 @@ def test_insufficient_balance_blob_tx(
[b"", b"\x00", b"\x01"],
ids=["no_calldata", "single_zero_calldata", "single_one_calldata"],
)
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 100])
@pytest.mark.parametrize("tx_max_fee_per_blob_gas_multiplier", [1, 100, 10000])
@pytest.mark.valid_from("Cancun")
def test_sufficient_balance_blob_tx(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def fork_block_excess_blob_gas(
fork: Fork,
pre_fork_excess_blobs: int,
pre_fork_blobs_per_block: int,
block_base_fee_per_gas: int,
) -> int:
"""Calculate the expected excess blob gas for the fork block."""
if pre_fork_blobs_per_block == 0:
Expand All @@ -139,6 +140,7 @@ def fork_block_excess_blob_gas(
return calc_excess_blob_gas_post_fork(
parent_excess_blobs=pre_fork_excess_blobs,
parent_blob_count=pre_fork_blobs_per_block,
parent_base_fee_per_gas=block_base_fee_per_gas,
)


Expand Down Expand Up @@ -382,6 +384,7 @@ def test_fork_transition_excess_blob_gas_at_blob_genesis(
),
],
)
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 16, 23])
def test_fork_transition_excess_blob_gas_post_blob_genesis(
blockchain_test: BlockchainTestFiller,
env: Environment,
Expand Down
8 changes: 8 additions & 0 deletions tests/osaka/eip7594_peerdas/test_get_blobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ def tx_gas() -> int:
return 21_000


@pytest.fixture
def block_base_fee_per_gas() -> int:
"""Return default max fee per gas for transactions sent during test."""
return 7


@pytest.fixture
def tx_calldata() -> bytes:
"""Calldata in transactions sent during test."""
Expand Down Expand Up @@ -83,6 +89,7 @@ def excess_blob_gas(
fork: Fork,
parent_excess_blobs: int | None,
parent_blobs: int | None,
block_base_fee_per_gas: int,
) -> int | None:
"""
Calculate the excess blob gas of the block under test from the parent block.
Expand All @@ -95,6 +102,7 @@ def excess_blob_gas(
return excess_blob_gas(
parent_excess_blobs=parent_excess_blobs,
parent_blob_count=parent_blobs,
parent_base_fee_per_gas=block_base_fee_per_gas,
)


Expand Down
1 change: 1 addition & 0 deletions tests/osaka/eip7918_blob_reserve_price/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Cross-client EIP-7918 Tests."""
Loading
Loading