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(forks,specs,tests): EIP-7691, EIP-4844: Refactor to use dynamic parameters #1023

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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 @@ -63,6 +63,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Add the `eest make env` command that generates a default env file (`env.yaml`)([#996](https://github.com/ethereum/execution-spec-tests/pull/996)).
- ✨ Generate Transaction Test type ([#933](https://github.com/ethereum/execution-spec-tests/pull/933)).
- ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)).
- ✨ Disable EIP-7742 framework changes for Prague ([#NNN](https://github.com/ethereum/execution-spec-tests/pull/NNN)).

### 🔧 EVM Tools

Expand Down
35 changes: 35 additions & 0 deletions docs/writing_tests/test_markers.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,41 @@ def test_something_with_all_tx_types_but_skip_type_1(state_test_only, tx_type):

In this example, the test will be skipped if `tx_type` is equal to 1 by returning a `pytest.mark.skip` marker, and return `None` otherwise.

## Custom Fork Covariant Markers

Custom fork covariant markers can be created by using the `fork_covariant_parametrize` decorator.

This decorator takes three arguments:

- `parameter_names`: A list of parameter names that will be parametrized using the custom function.
- `fn`: A function that takes the fork as parameter and returns a list of values that will be used to parametrize the test.
- `marks`: A marker, list of markers, or a lambda function that can be used to add additional markers to the test.

```python
import pytest

from pytest_plugins import fork_covariant_parametrize

def covariant_function(fork):
return [[1, 2], [3, 4]] if fork.name() == "Paris" else [[4, 5], [5, 6], [6, 7]]

@fork_covariant_parametrize(parameter_names=[
"test_parameter", "test_parameter_2"
], fn=covariant_function)
@pytest.mark.valid_from("Paris")
@pytest.mark.valid_until("Shanghai")
def test_case(state_test_only, test_parameter, test_parameter_2):
pass
```

In this example, the test will be parametrized with the values `[1, 2]` and `[3, 4]` for the Paris fork, with values `1` and `3` being assigned to `test_parameter` and `2` and `4` being assigned to `test_parameter_2`. For the Shanghai fork, the test will be parametrized with the values `[4, 5]`, `[5, 6]`, and `[6, 7]`. Therefore, more test cases will be generated for the Shanghai fork.

If the parameters that are being parametrized is only a single parameter, the return value of `fn` should be a list of values for that parameter.

If the parameters that are being parametrized are multiple, the return value of `fn` should be a list of tuples/lists, where each tuple contains the values for each parameter.

The function can also return a list of `pytest.param` objects, which allows for additional markers and test IDs to be added to the test.

## Fill/Execute Markers

These markers are used to apply different markers to a test depending on whether it is being filled or executed.
Expand Down
1 change: 0 additions & 1 deletion src/ethereum_test_fixtures/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ def from_fixture_header(
List[Hash],
Hash,
List[Bytes],
HexNumber,
]

# Important: We check EngineNewPayloadV3Parameters first as it has more fields, and pydantic
Expand Down
15 changes: 4 additions & 11 deletions src/ethereum_test_fixtures/tests/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
Bytes,
Hash,
HeaderNonce,
HexNumber,
TestPrivateKey,
ZeroPaddedHexNumber,
to_json,
Expand Down Expand Up @@ -669,7 +668,6 @@
excess_blob_gas=18,
parent_beacon_block_root=19,
requests_hash=20,
target_blobs_per_block=21,
),
transactions=[
Transaction(
Expand Down Expand Up @@ -731,7 +729,7 @@
"blobGasUsed": hex(17),
"excessBlobGas": hex(18),
"blockHash": (
"0x9f6459fb2eca2b75ee861e97d679ba91457bb446c8484a7ad76d1675a7f78fde"
"0x93bd662d8a80a1f54bffc6d140b83d6cda233209998809f9540be51178b4d0b6"
),
"transactions": [
Transaction(
Expand Down Expand Up @@ -789,9 +787,8 @@
),
).requests_list
],
HexNumber(21).hex(),
],
"forkchoiceUpdatedVersion": "4",
"forkchoiceUpdatedVersion": "3",
"newPayloadVersion": "4",
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
Expand Down Expand Up @@ -826,7 +823,6 @@
excess_blob_gas=18,
parent_beacon_block_root=19,
requests_hash=20,
target_blobs_per_block=21,
),
transactions=[
Transaction(
Expand Down Expand Up @@ -887,7 +883,7 @@
"blobGasUsed": hex(17),
"excessBlobGas": hex(18),
"blockHash": (
"0x9f6459fb2eca2b75ee861e97d679ba91457bb446c8484a7ad76d1675a7f78fde"
"0x93bd662d8a80a1f54bffc6d140b83d6cda233209998809f9540be51178b4d0b6"
),
"transactions": [
Transaction(
Expand Down Expand Up @@ -945,10 +941,9 @@
),
).requests_list
],
HexNumber(21).hex(),
],
"newPayloadVersion": "4",
"forkchoiceUpdatedVersion": "4",
"forkchoiceUpdatedVersion": "3",
"validationError": "BlockException.INCORRECT_BLOCK_FORMAT"
"|TransactionException.INTRINSIC_GAS_TOO_LOW",
},
Expand Down Expand Up @@ -1232,7 +1227,6 @@ def test_json_deserialization(
target_pubkey=BLSPublicKey(2),
),
).requests_list,
HexNumber(9),
),
[
{
Expand Down Expand Up @@ -1298,7 +1292,6 @@ def test_json_deserialization(
),
).requests_list
],
HexNumber(9).hex(),
],
id="fixture_engine_new_payload_parameters_v4",
),
Expand Down
95 changes: 82 additions & 13 deletions src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __call__(self, block_number: int = 0, timestamp: int = 0) -> Any:

class MemoryExpansionGasCalculator(Protocol):
"""
A protocol to calculate the gas cost of memory expansion for a given fork.
A protocol to calculate the gas cost of memory expansion at a given fork.
"""

def __call__(self, *, new_bytes: int, previous_bytes: int = 0) -> int:
Expand All @@ -41,7 +41,7 @@ def __call__(self, *, new_bytes: int, previous_bytes: int = 0) -> int:

class CalldataGasCalculator(Protocol):
"""
A protocol to calculate the transaction gas cost of calldata for a given fork.
A protocol to calculate the transaction gas cost of calldata at a given fork.
"""

def __call__(self, *, data: BytesConvertible, floor: bool = False) -> int:
Expand All @@ -65,7 +65,7 @@ def __call__(self, *, data: BytesConvertible) -> int:

class TransactionIntrinsicCostCalculator(Protocol):
"""
A protocol to calculate the intrinsic gas cost of a transaction for a given fork.
A protocol to calculate the intrinsic gas cost of a transaction at a given fork.
"""

def __call__(
Expand Down Expand Up @@ -97,6 +97,37 @@ def __call__(
pass


class BlobGasPriceCalculator(Protocol):
"""
A protocol to calculate the blob gas price given the excess blob gas at a given fork.
"""

def __call__(self, *, excess_blob_gas: int) -> int:
"""
Returns the blob gas price given the excess blob gas.
"""
pass


class ExcessBlobGasCalculator(Protocol):
"""
A protocol to calculate the excess blob gas for a block at a given fork.
"""

def __call__(
self,
*,
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,
) -> int:
"""
Returns the excess blob gas given the parent's excess blob gas and blob gas used.
"""
pass


class BaseForkMeta(ABCMeta):
"""
Metaclass for BaseFork
Expand Down Expand Up @@ -218,20 +249,30 @@ def header_blob_gas_used_required(cls, block_number: int = 0, timestamp: int = 0

@classmethod
@abstractmethod
def header_beacon_root_required(cls, block_number: int, timestamp: int) -> bool:
def header_beacon_root_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Returns true if the header must contain parent beacon block root
"""
pass

@classmethod
@abstractmethod
def header_requests_required(cls, block_number: int, timestamp: int) -> bool:
def header_requests_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Returns true if the header must contain beacon chain requests
"""
pass

@classmethod
@abstractmethod
def header_target_blobs_per_block_required(
cls, block_number: int = 0, timestamp: int = 0
) -> bool:
"""
Returns true if the header must contain target blobs per block.
"""
pass

# Gas related abstract methods

@classmethod
Expand Down Expand Up @@ -285,33 +326,61 @@ def transaction_intrinsic_cost_calculator(

@classmethod
@abstractmethod
def header_target_blobs_per_block_required(cls, block_number: int, timestamp: int) -> bool:
def blob_gas_price_calculator(
cls, block_number: int = 0, timestamp: int = 0
) -> BlobGasPriceCalculator:
"""
Returns true if the header must contain target blobs per block.
Returns a callable that calculates the blob gas price at a given fork.
"""
pass

@classmethod
@abstractmethod
def excess_blob_gas_calculator(
cls, block_number: int = 0, timestamp: int = 0
) -> ExcessBlobGasCalculator:
"""
Returns a callable that calculates the excess blob gas for a block at a given fork.
"""
pass

@classmethod
@abstractmethod
def min_base_fee_per_blob_gas(cls, block_number: int = 0, timestamp: int = 0) -> int:
"""
Returns the minimum base fee per blob gas at a given fork.
"""
pass

@classmethod
@abstractmethod
def blob_gas_per_blob(cls, block_number: int = 0, timestamp: int = 0) -> int:
"""
Returns the amount of blob gas used per blob at a given fork.
"""
pass

@classmethod
@abstractmethod
def blob_gas_per_blob(cls, block_number: int, timestamp: int) -> int:
def blob_base_fee_update_fraction(cls, block_number: int = 0, timestamp: int = 0) -> int:
"""
Returns the amount of blob gas used per blob for a given fork.
Returns the blob base fee update fraction at a given fork.
"""
pass

@classmethod
@abstractmethod
def target_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
def target_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
"""
Returns the target blobs per block for a given fork.
Returns the target blobs per block at a given fork.
"""
pass

@classmethod
@abstractmethod
def max_blobs_per_block(cls, block_number: int, timestamp: int) -> int:
def max_blobs_per_block(cls, block_number: int = 0, timestamp: int = 0) -> int:
"""
Returns the max blobs per block for a given fork.
Returns the max blobs per block at a given fork.
"""
pass

Expand Down
Loading
Loading