Skip to content
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

feat(tests): EIP-7691: Fork transition tests #1082

Merged
merged 6 commits into from
Jan 22, 2025
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
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"),
danceratopz marked this conversation as resolved.
Show resolved Hide resolved
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",
)
Loading