Skip to content

Commit 5791a72

Browse files
authored
feat(tests): EIP-7918 - Blob base fee bounded by execution cost (#1685)
* feat(forks|tests): eip-7918 initial tests plus eip-4844 adjustments. * chore(tests): extra param and docstring tweak.
1 parent 0988892 commit 5791a72

File tree

12 files changed

+538
-8
lines changed

12 files changed

+538
-8
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Users can select any of the artifacts depending on their testing needs for their
7474
- ✨ Introduce blockchain tests for ZKEVM to cover the scenario of pure ether transfers [#1742](https://github.com/ethereum/execution-spec-tests/pull/1742).
7575
-[EIP-7934](https://eips.ethereum.org/EIPS/eip-7934): Add test cases for the block RLP max limit of 10MiB ([#1730](https://github.com/ethereum/execution-spec-tests/pull/1730)).
7676
-[EIP-7939](https://eips.ethereum.org/EIPS/eip-7939) Add count leading zeros (CLZ) opcode tests for Osaka ([#1733](https://github.com/ethereum/execution-spec-tests/pull/1733)).
77+
-[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)).
7778

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

src/ethereum_test_forks/base_fork.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def __call__(
9696
parent_excess_blobs: int | None = None,
9797
parent_blob_gas_used: int | None = None,
9898
parent_blob_count: int | None = None,
99+
parent_base_fee_per_gas: int,
99100
) -> int:
100101
"""Return the excess blob gas given the parent's excess blob gas and blob gas used."""
101102
pass

src/ethereum_test_forks/forks/forks.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@ def fn(
960960
parent_excess_blobs: int | None = None,
961961
parent_blob_gas_used: int | None = None,
962962
parent_blob_count: int | None = None,
963+
parent_base_fee_per_gas: int, # Required for Osaka as using this as base
963964
) -> int:
964965
if parent_excess_blob_gas is None:
965966
assert parent_excess_blobs is not None, "Parent excess blobs are required"
@@ -1388,6 +1389,55 @@ def precompiles(cls, block_number: int = 0, timestamp: int = 0) -> List[Address]
13881389
"""
13891390
return [Address(0x100)] + super(Osaka, cls).precompiles(block_number, timestamp)
13901391

1392+
@classmethod
1393+
def excess_blob_gas_calculator(
1394+
cls, block_number: int = 0, timestamp: int = 0
1395+
) -> ExcessBlobGasCalculator:
1396+
"""Return a callable that calculates the excess blob gas for a block."""
1397+
target_blobs_per_block = cls.target_blobs_per_block(block_number, timestamp)
1398+
blob_gas_per_blob = cls.blob_gas_per_blob(block_number, timestamp)
1399+
target_blob_gas_per_block = target_blobs_per_block * blob_gas_per_blob
1400+
max_blobs_per_block = cls.max_blobs_per_block(block_number, timestamp)
1401+
blob_base_cost = 2**14 # EIP-7918 new parameter
1402+
1403+
def fn(
1404+
*,
1405+
parent_excess_blob_gas: int | None = None,
1406+
parent_excess_blobs: int | None = None,
1407+
parent_blob_gas_used: int | None = None,
1408+
parent_blob_count: int | None = None,
1409+
parent_base_fee_per_gas: int, # EIP-7918 additional parameter
1410+
) -> int:
1411+
if parent_excess_blob_gas is None:
1412+
assert parent_excess_blobs is not None, "Parent excess blobs are required"
1413+
parent_excess_blob_gas = parent_excess_blobs * blob_gas_per_blob
1414+
if parent_blob_gas_used is None:
1415+
assert parent_blob_count is not None, "Parent blob count is required"
1416+
parent_blob_gas_used = parent_blob_count * blob_gas_per_blob
1417+
if parent_excess_blob_gas + parent_blob_gas_used < target_blob_gas_per_block:
1418+
return 0
1419+
1420+
# EIP-7918: Apply reserve price when execution costs dominate blob costs
1421+
current_blob_base_fee = cls.blob_gas_price_calculator()(
1422+
excess_blob_gas=parent_excess_blob_gas
1423+
)
1424+
reserve_price_active = (
1425+
blob_base_cost * parent_base_fee_per_gas
1426+
> blob_gas_per_blob * current_blob_base_fee
1427+
)
1428+
if reserve_price_active:
1429+
blob_excess_adjustment = (
1430+
parent_blob_gas_used
1431+
* (max_blobs_per_block - target_blobs_per_block)
1432+
// max_blobs_per_block
1433+
)
1434+
return parent_excess_blob_gas + blob_excess_adjustment
1435+
1436+
# Original EIP-4844 calculation
1437+
return parent_excess_blob_gas + parent_blob_gas_used - target_blob_gas_per_block
1438+
1439+
return fn
1440+
13911441

13921442
class EOFv1(Prague, solc_name="cancun"):
13931443
"""EOF fork."""

tests/cancun/eip4844_blobs/conftest.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def excess_blob_gas(
7070
fork: Fork,
7171
parent_excess_blobs: int | None,
7272
parent_blobs: int | None,
73+
block_base_fee_per_gas: int,
7374
) -> int | None:
7475
"""
7576
Calculate the excess blob gas of the block under test from the parent block.
@@ -78,10 +79,10 @@ def excess_blob_gas(
7879
"""
7980
if parent_excess_blobs is None or parent_blobs is None:
8081
return None
81-
excess_blob_gas = fork.excess_blob_gas_calculator()
82-
return excess_blob_gas(
82+
return fork.excess_blob_gas_calculator()(
8383
parent_excess_blobs=parent_excess_blobs,
8484
parent_blob_count=parent_blobs,
85+
parent_base_fee_per_gas=block_base_fee_per_gas,
8586
)
8687

8788

@@ -90,6 +91,7 @@ def correct_excess_blob_gas(
9091
fork: Fork,
9192
parent_excess_blobs: int | None,
9293
parent_blobs: int | None,
94+
block_base_fee_per_gas: int,
9395
) -> int:
9496
"""
9597
Calculate the correct excess blob gas of the block under test from the parent block.
@@ -98,18 +100,19 @@ def correct_excess_blob_gas(
98100
"""
99101
if parent_excess_blobs is None or parent_blobs is None:
100102
return 0
101-
excess_blob_gas = fork.excess_blob_gas_calculator()
102-
return excess_blob_gas(
103+
return fork.excess_blob_gas_calculator()(
103104
parent_excess_blobs=parent_excess_blobs,
104105
parent_blob_count=parent_blobs,
106+
parent_base_fee_per_gas=block_base_fee_per_gas,
105107
)
106108

107109

108110
@pytest.fixture
109-
def block_fee_per_blob_gas( # noqa: D103
111+
def block_fee_per_blob_gas(
110112
fork: Fork,
111113
correct_excess_blob_gas: int,
112114
) -> int:
115+
"""Calculate the blob gas price for the current block."""
113116
get_blob_gas_price = fork.blob_gas_price_calculator()
114117
return get_blob_gas_price(excess_blob_gas=correct_excess_blob_gas)
115118

@@ -251,7 +254,7 @@ def non_zero_blob_gas_used_genesis_block(
251254
genesis_excess_blob_gas: int,
252255
parent_excess_blob_gas: int,
253256
tx_max_fee_per_gas: int,
254-
target_blobs_per_block: int,
257+
block_base_fee_per_gas: int,
255258
) -> Block | None:
256259
"""
257260
For test cases with a non-zero blobGasUsed field in the
@@ -271,10 +274,17 @@ def non_zero_blob_gas_used_genesis_block(
271274
return None
272275

273276
excess_blob_gas_calculator = fork.excess_blob_gas_calculator(block_number=1)
274-
assert parent_excess_blob_gas == excess_blob_gas_calculator(
277+
calculated_excess_blob_gas = excess_blob_gas_calculator(
275278
parent_excess_blob_gas=genesis_excess_blob_gas,
276279
parent_blob_count=0,
277-
), "parent excess blob gas is not as expected for extra block"
280+
parent_base_fee_per_gas=block_base_fee_per_gas,
281+
)
282+
283+
assert parent_excess_blob_gas == calculated_excess_blob_gas, (
284+
f"parent excess blob gas mismatch: expected {parent_excess_blob_gas}, "
285+
f"got {calculated_excess_blob_gas} for {parent_blobs} blobs "
286+
f"with base_fee_per_gas {block_base_fee_per_gas}"
287+
)
278288

279289
sender = pre.fund_eoa(10**27)
280290

tests/cancun/eip4844_blobs/spec.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class Spec:
4545
# LIMIT_BLOBS_PER_TX = 2**12
4646
HASH_OPCODE_BYTE = 0x49
4747
HASH_GAS_COST = 3
48+
GAS_PER_BLOB = 2**17
4849

4950
@classmethod
5051
def kzg_to_versioned_hash(

tests/cancun/eip4844_blobs/test_blob_txs.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ def expected_excess_blob_gas(
312312
parent_blobs: Optional[int],
313313
block_number: int,
314314
block_timestamp: int,
315+
block_base_fee_per_gas: int,
315316
) -> Optional[int | Removable]:
316317
"""Calculate blob gas used by the test block."""
317318
if not fork.header_excess_blob_gas_required(
@@ -322,6 +323,7 @@ def expected_excess_blob_gas(
322323
return excess_blob_gas(
323324
parent_excess_blobs=parent_excess_blobs if parent_excess_blobs else 0,
324325
parent_blob_count=parent_blobs if parent_blobs else 0,
326+
parent_base_fee_per_gas=block_base_fee_per_gas,
325327
)
326328

327329

@@ -374,6 +376,7 @@ def block(
374376
"blobs_per_tx",
375377
SpecHelpers.all_valid_blob_combinations,
376378
)
379+
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 100])
377380
@pytest.mark.valid_from("Cancun")
378381
def test_valid_blob_tx_combinations(
379382
blockchain_test: BlockchainTestFiller,
@@ -664,6 +667,7 @@ def test_insufficient_balance_blob_tx(
664667
[b"", b"\x00", b"\x01"],
665668
ids=["no_calldata", "single_zero_calldata", "single_one_calldata"],
666669
)
670+
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 100])
667671
@pytest.mark.parametrize("tx_max_fee_per_blob_gas_multiplier", [1, 100, 10000])
668672
@pytest.mark.valid_from("Cancun")
669673
def test_sufficient_balance_blob_tx(

tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def fork_block_excess_blob_gas(
131131
fork: Fork,
132132
pre_fork_excess_blobs: int,
133133
pre_fork_blobs_per_block: int,
134+
block_base_fee_per_gas: int,
134135
) -> int:
135136
"""Calculate the expected excess blob gas for the fork block."""
136137
if pre_fork_blobs_per_block == 0:
@@ -139,6 +140,7 @@ def fork_block_excess_blob_gas(
139140
return calc_excess_blob_gas_post_fork(
140141
parent_excess_blobs=pre_fork_excess_blobs,
141142
parent_blob_count=pre_fork_blobs_per_block,
143+
parent_base_fee_per_gas=block_base_fee_per_gas,
142144
)
143145

144146

@@ -382,6 +384,7 @@ def test_fork_transition_excess_blob_gas_at_blob_genesis(
382384
),
383385
],
384386
)
387+
@pytest.mark.parametrize("block_base_fee_per_gas", [7, 16, 23])
385388
def test_fork_transition_excess_blob_gas_post_blob_genesis(
386389
blockchain_test: BlockchainTestFiller,
387390
env: Environment,

tests/osaka/eip7594_peerdas/test_get_blobs.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ def tx_gas() -> int:
5151
return 21_000
5252

5353

54+
@pytest.fixture
55+
def block_base_fee_per_gas() -> int:
56+
"""Return default max fee per gas for transactions sent during test."""
57+
return 7
58+
59+
5460
@pytest.fixture
5561
def tx_calldata() -> bytes:
5662
"""Calldata in transactions sent during test."""
@@ -83,6 +89,7 @@ def excess_blob_gas(
8389
fork: Fork,
8490
parent_excess_blobs: int | None,
8591
parent_blobs: int | None,
92+
block_base_fee_per_gas: int,
8693
) -> int | None:
8794
"""
8895
Calculate the excess blob gas of the block under test from the parent block.
@@ -95,6 +102,7 @@ def excess_blob_gas(
95102
return excess_blob_gas(
96103
parent_excess_blobs=parent_excess_blobs,
97104
parent_blob_count=parent_blobs,
105+
parent_base_fee_per_gas=block_base_fee_per_gas,
98106
)
99107

100108

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Cross-client EIP-7918 Tests."""

0 commit comments

Comments
 (0)