diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6465f14da79..504bfa04138 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -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 diff --git a/tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py b/tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py index b8813947fa2..39b2b0bcbe4 100644 --- a/tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py +++ b/tests/cancun/eip4844_blobs/test_excess_blob_gas_fork_transition.py @@ -8,10 +8,12 @@ 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, @@ -19,7 +21,6 @@ Environment, Hash, Header, - TestAddress, Transaction, add_kzg_version, ) @@ -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 @@ -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", [ @@ -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, @@ -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", [ @@ -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, @@ -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], @@ -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", )