Skip to content

Commit

Permalink
feat(tests): EIP-7691: Fork transition tests (ethereum#1082)
Browse files Browse the repository at this point in the history
* refactor(tests): EIP-4844, EIP-7691: Fill fork transition blob tests in newer forks

* fix(tests): Rebase fixes, add more checks to the test

* chengelog

* feat(tests): more blob gas tests for fork transitions (ethereum#1107)

* feat(tests): more edge cases for blob gas at transitions

* Apply suggestions from code review

---------

Co-authored-by: Mario Vega <[email protected]>

* fix(tests): Remove type-2 txs, destination account is empty

* tox: typing

---------

Co-authored-by: danceratopz <[email protected]>
  • Loading branch information
2 people authored and codeofcarson committed Jan 24, 2025
1 parent 36f40b3 commit 480c960
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 59 deletions.
2 changes: 1 addition & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Release tarball changes:
- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- 🔀 Update EIP-2935 according to [spec updates](https://github.com/ethereum/EIPs/pull/9144) ([#1046](https://github.com/ethereum/execution-spec-tests/pull/1046))
-[EIP-7691](https://eips.ethereum.org/EIPS/eip-7691) Blob throughput increase tests by parametrization of existing EIP-4844 tests ([#1023](https://github.com/ethereum/execution-spec-tests/pull/1023))
-[EIP-7691](https://eips.ethereum.org/EIPS/eip-7691) Blob throughput increase tests by parametrization of existing EIP-4844 tests ([#1023](https://github.com/ethereum/execution-spec-tests/pull/1023), [#1082](https://github.com/ethereum/execution-spec-tests/pull/1082))

### 🛠️ Framework

Expand Down
266 changes: 208 additions & 58 deletions tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@

import pytest

from ethereum_test_forks import Cancun, Fork
from ethereum_test_forks import Fork
from ethereum_test_tools import (
EOA,
Account,
Address,
Alloc,
Block,
BlockchainTestFiller,
BlockException,
EngineAPIError,
Environment,
Hash,
Header,
TestAddress,
Transaction,
add_kzg_version,
)
Expand All @@ -29,10 +30,6 @@
REFERENCE_SPEC_GIT_PATH = ref_spec_4844.git_path
REFERENCE_SPEC_VERSION = ref_spec_4844.version

# All tests run on the transition fork from Shanghai to Cancun
pytestmark = pytest.mark.valid_at_transition_to("Cancun")


# Timestamp of the fork
FORK_TIMESTAMP = 15_000

Expand All @@ -43,89 +40,171 @@ def env() -> Environment: # noqa: D103


@pytest.fixture
def pre() -> Mapping[Address, Account]: # noqa: D103
return {
TestAddress: Account(balance=10**40),
}
def pre_fork_blobs_per_block(fork: Fork) -> int:
"""Amount of blobs to produce with the pre-fork rules."""
if fork.supports_blobs(timestamp=0):
return fork.max_blobs_per_block(timestamp=0)
return 0


@pytest.fixture
def sender(pre: Alloc) -> EOA:
"""Sender account."""
return pre.fund_eoa()


@pytest.fixture
def pre_fork_blocks():
def pre_fork_blocks(
pre_fork_blobs_per_block: int,
destination_account: Address,
sender: EOA,
) -> List[Block]:
"""Generate blocks to reach the fork."""
return [Block(timestamp=t) for t in range(999, FORK_TIMESTAMP, 1_000)]
return [
Block(
txs=[
Transaction(
ty=Spec.BLOB_TX_TYPE,
to=destination_account,
value=1,
gas_limit=3_000_000,
max_fee_per_gas=1_000_000,
max_priority_fee_per_gas=10,
max_fee_per_blob_gas=100,
access_list=[],
blob_versioned_hashes=add_kzg_version(
[Hash(x) for x in range(pre_fork_blobs_per_block)],
Spec.BLOB_COMMITMENT_VERSION_KZG,
),
sender=sender,
)
]
if pre_fork_blobs_per_block > 0
else [],
timestamp=t,
)
for t in range(999, FORK_TIMESTAMP, 1_000)
]


@pytest.fixture
def pre_fork_excess_blobs(
fork: Fork,
pre_fork_blobs_per_block: int,
pre_fork_blocks: List[Block],
) -> int:
"""
Return the cummulative excess blobs up until the fork given the pre_fork_blobs_per_block
and the target blobs in the fork prior.
"""
if not fork.supports_blobs(timestamp=0):
return 0
target_blobs = fork.target_blobs_per_block(timestamp=0)
if pre_fork_blobs_per_block > target_blobs:
return (pre_fork_blobs_per_block - target_blobs) * (len(pre_fork_blocks) - 1)
return 0


@pytest.fixture
def post_fork_block_count(fork: Fork) -> int:
"""Amount of blocks to produce with the post-fork rules."""
return SpecHelpers.get_min_excess_blobs_for_blob_gas_price(fork=fork, blob_gas_price=2) // (
fork.max_blobs_per_block() - fork.target_blobs_per_block()
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP)
- fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP)
)


@pytest.fixture
def blob_count_per_block() -> int:
def post_fork_blobs_per_block(fork: Fork) -> int:
"""Amount of blocks to produce with the post-fork rules."""
return 4
return fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP) + 1


@pytest.fixture
def destination_account() -> Address: # noqa: D103
return Address(0x100)
def destination_account(pre: Alloc) -> Address: # noqa: D103
# Empty account to receive the blobs
return pre.fund_eoa(amount=0)


@pytest.fixture
def fork_block_excess_blob_gas(
fork: Fork,
pre_fork_excess_blobs: int,
pre_fork_blobs_per_block: int,
) -> int:
"""Calculate the expected excess blob gas for the fork block."""
if pre_fork_blobs_per_block == 0:
return 0
calc_excess_blob_gas_post_fork = fork.excess_blob_gas_calculator(timestamp=FORK_TIMESTAMP)
return calc_excess_blob_gas_post_fork(
parent_excess_blobs=pre_fork_excess_blobs,
parent_blob_count=pre_fork_blobs_per_block,
)


@pytest.fixture
def post_fork_blocks(
destination_account: Address,
post_fork_block_count: int,
blob_count_per_block: int,
post_fork_blobs_per_block: int,
fork_block_excess_blob_gas: int,
sender: EOA,
):
"""Generate blocks past the fork."""
return [
Block(
txs=[
blocks = []
for i in range(post_fork_block_count):
txs = (
[
Transaction(
ty=Spec.BLOB_TX_TYPE,
nonce=b,
to=destination_account,
value=1,
gas_limit=3000000,
max_fee_per_gas=1000000,
gas_limit=3_000_000,
max_fee_per_gas=1_000_000,
max_priority_fee_per_gas=10,
max_fee_per_blob_gas=100,
access_list=[],
blob_versioned_hashes=add_kzg_version(
[Hash(x) for x in range(blob_count_per_block)],
[Hash(x) for x in range(post_fork_blobs_per_block)],
Spec.BLOB_COMMITMENT_VERSION_KZG,
),
sender=sender,
)
if blob_count_per_block > 0
else Transaction(
ty=2,
nonce=b,
to=destination_account,
value=1,
gas_limit=3000000,
max_fee_per_gas=1000000,
max_priority_fee_per_gas=10,
access_list=[],
)
],
]
if post_fork_blobs_per_block > 0
else []
)
for b in range(post_fork_block_count)
]
if i == 0:
# Check the excess blob gas on the first block of the new fork
blocks.append(
Block(
txs=txs,
excess_blob_gas=fork_block_excess_blob_gas,
)
)
else:
blocks.append(Block(txs=txs))
return blocks


@pytest.fixture
def post( # noqa: D103
pre_fork_blocks: List[Block],
pre_fork_blobs_per_block: int,
post_fork_block_count: int,
post_fork_blobs_per_block: int,
destination_account: Address,
) -> Mapping[Address, Account]:
pre_fork_value = len(pre_fork_blocks) if pre_fork_blobs_per_block > 0 else 0
post_fork_value = post_fork_block_count if post_fork_blobs_per_block > 0 else 0
total_value = pre_fork_value + post_fork_value
if total_value == 0:
return {}
return {
destination_account: Account(balance=post_fork_block_count),
destination_account: Account(balance=total_value),
}


@pytest.mark.valid_at_transition_to("Cancun")
@pytest.mark.parametrize(
"excess_blob_gas_present,blob_gas_used_present",
[
Expand All @@ -137,7 +216,7 @@ def post( # noqa: D103
def test_invalid_pre_fork_block_with_blob_fields(
blockchain_test: BlockchainTestFiller,
env: Environment,
pre: Mapping[Address, Account],
pre: Alloc,
pre_fork_blocks: List[Block],
excess_blob_gas_present: bool,
blob_gas_used_present: bool,
Expand Down Expand Up @@ -166,10 +245,10 @@ def test_invalid_pre_fork_block_with_blob_fields(
)
],
genesis_environment=env,
tag="invalid_pre_fork_blob_fields",
)


@pytest.mark.valid_at_transition_to("Cancun")
@pytest.mark.parametrize(
"excess_blob_gas_missing,blob_gas_used_missing",
[
Expand All @@ -181,7 +260,7 @@ def test_invalid_pre_fork_block_with_blob_fields(
def test_invalid_post_fork_block_without_blob_fields(
blockchain_test: BlockchainTestFiller,
env: Environment,
pre: Mapping[Address, Account],
pre: Alloc,
pre_fork_blocks: List[Block],
excess_blob_gas_missing: bool,
blob_gas_used_missing: bool,
Expand Down Expand Up @@ -211,28 +290,100 @@ def test_invalid_post_fork_block_without_blob_fields(
)
],
genesis_environment=env,
tag="blob_fields_missing_post_fork",
)


@pytest.mark.parametrize(
"post_fork_block_count,blob_count_per_block",
[
(
SpecHelpers.get_min_excess_blobs_for_blob_gas_price(fork=Cancun, blob_gas_price=2)
// (Cancun.max_blobs_per_block() - Cancun.target_blobs_per_block())
@pytest.mark.valid_at_transition_to("Cancun", subsequent_forks=False)
@pytest.mark.parametrize_by_fork(
"post_fork_block_count,post_fork_blobs_per_block",
lambda fork: [
pytest.param(
SpecHelpers.get_min_excess_blobs_for_blob_gas_price(fork=fork, blob_gas_price=2)
// (
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP)
- fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP)
)
+ 2,
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP),
id="max_blobs",
),
pytest.param(10, 0, id="no_blobs"),
pytest.param(10, fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP), id="target_blobs"),
],
)
def test_fork_transition_excess_blob_gas_at_blob_genesis(
blockchain_test: BlockchainTestFiller,
env: Environment,
pre: Alloc,
pre_fork_blocks: List[Block],
post_fork_blocks: List[Block],
post: Mapping[Address, Account],
):
"""
Test `excessBlobGas` calculation in the header when the fork is activated.
Also produce enough blocks to test the blob gas price increase when the block is full with
`SpecHelpers.max_blobs_per_block()` blobs.
"""
blockchain_test(
pre=pre,
post=post,
blocks=pre_fork_blocks + post_fork_blocks,
genesis_environment=env,
)


@pytest.mark.valid_at_transition_to("Prague", subsequent_forks=True)
@pytest.mark.parametrize_by_fork(
"post_fork_block_count,pre_fork_blobs_per_block,post_fork_blobs_per_block",
lambda fork: [
pytest.param(
SpecHelpers.get_min_excess_blobs_for_blob_gas_price(fork=fork, blob_gas_price=2)
// (
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP)
- fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP)
)
+ 2,
Cancun.max_blobs_per_block(),
fork.max_blobs_per_block(timestamp=0),
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP),
id="max_blobs",
),
pytest.param(
10,
0,
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP),
id="no_blobs_before",
),
pytest.param(
10,
fork.max_blobs_per_block(timestamp=0),
0,
id="no_blobs_after",
),
pytest.param(
10,
fork.target_blobs_per_block(timestamp=0),
fork.target_blobs_per_block(timestamp=FORK_TIMESTAMP),
id="target_blobs",
),
pytest.param(
10,
1,
fork.max_blobs_per_block(timestamp=FORK_TIMESTAMP),
id="single_blob_to_max_blobs",
),
pytest.param(
10,
fork.max_blobs_per_block(timestamp=0),
1,
id="max_blobs_to_single_blob",
),
(10, 0),
(10, Cancun.target_blobs_per_block()),
],
ids=["max_blobs", "no_blobs", "target_blobs"],
)
def test_fork_transition_excess_blob_gas(
def test_fork_transition_excess_blob_gas_post_blob_genesis(
blockchain_test: BlockchainTestFiller,
env: Environment,
pre: Mapping[Address, Account],
pre: Alloc,
pre_fork_blocks: List[Block],
post_fork_blocks: List[Block],
post: Mapping[Address, Account],
Expand All @@ -248,5 +399,4 @@ def test_fork_transition_excess_blob_gas(
post=post,
blocks=pre_fork_blocks + post_fork_blocks,
genesis_environment=env,
tag="correct_initial_blob_gas_calc",
)

0 comments on commit 480c960

Please sign in to comment.