diff --git a/setup.py b/setup.py index 8c2ec072d..de446bb1c 100644 --- a/setup.py +++ b/setup.py @@ -618,14 +618,13 @@ from eth2spec.bellatrix import {preset_name} as bellatrix # # EIP4844SpecBuilder # -class EIP4844SpecBuilder(BellatrixSpecBuilder): +class EIP4844SpecBuilder(CapellaSpecBuilder): fork: str = EIP4844 @classmethod def imports(cls, preset_name: str): return super().imports(preset_name) + f''' -from eth2spec.utils import kzg -from eth2spec.bellatrix import {preset_name} as bellatrix +from eth2spec.capella import {preset_name} as capella ''' @@ -638,19 +637,31 @@ T = TypeVar('T') # For generic function @classmethod def sundry_functions(cls) -> str: return super().sundry_functions() + '\n\n' + ''' -# TODO: for mainnet, load pre-generated trusted setup file to reduce building time. -# TESTING_FIELD_ELEMENTS_PER_BLOB is hardcoded copy from minimal presets -TESTING_FIELD_ELEMENTS_PER_BLOB = 4 -TESTING_SECRET = 1337 -TESTING_KZG_SETUP_G1 = kzg.generate_setup(bls.G1, TESTING_SECRET, TESTING_FIELD_ELEMENTS_PER_BLOB) -TESTING_KZG_SETUP_G2 = kzg.generate_setup(bls.G2, TESTING_SECRET, TESTING_FIELD_ELEMENTS_PER_BLOB) -TESTING_KZG_SETUP_LAGRANGE = kzg.get_lagrange(TESTING_KZG_SETUP_G1) +# +# Temporarily disable Withdrawals functions for EIP4844 testnets +# -KZG_SETUP_G1 = [bls.G1_to_bytes48(p) for p in TESTING_KZG_SETUP_G1] -KZG_SETUP_G2 = [bls.G2_to_bytes96(p) for p in TESTING_KZG_SETUP_G2] -KZG_SETUP_LAGRANGE = TESTING_KZG_SETUP_LAGRANGE -ROOTS_OF_UNITY = kzg.compute_roots_of_unity(TESTING_FIELD_ELEMENTS_PER_BLOB) +def no_op(fn): # type: ignore + def wrapper(*args, **kw): # type: ignore + return None + return wrapper + + +def get_empty_list_result(fn): # type: ignore + def wrapper(*args, **kw): # type: ignore + return [] + return wrapper + + +process_withdrawals = no_op(process_withdrawals) +process_bls_to_execution_change = no_op(process_bls_to_execution_change) +get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals) + + +# +# End +# def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]: return "TEST"''' @@ -1002,7 +1013,7 @@ class PySpecCommand(Command): specs/bellatrix/p2p-interface.md sync/optimistic.md """ - if self.spec_fork == CAPELLA: + if self.spec_fork in (CAPELLA, EIP4844): self.md_doc_paths += """ specs/capella/beacon-chain.md specs/capella/fork.md diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index d7d19b613..afab21dca 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -33,13 +33,14 @@ - [`process_execution_payload`](#process_execution_payload) - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) + - [Disabling Withdrawals](#disabling-withdrawals) ## Introduction -This upgrade adds blobs to the beacon chain as part of EIP-4844. +This upgrade adds blobs to the beacon chain as part of EIP-4844. This is an extension of the Capella upgrade. ## Custom types @@ -88,7 +89,8 @@ class BeaconBlockBody(Container): voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] sync_aggregate: SyncAggregate # Execution - execution_payload: ExecutionPayload + execution_payload: ExecutionPayload # [Modified in EIP-4844] + bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] # [New in EIP-4844] ``` @@ -109,10 +111,11 @@ class ExecutionPayload(Container): timestamp: uint64 extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: uint256 - excess_blobs: uint64 # [New in EIP-4844] + excess_data_gas: uint256 # [New in EIP-4844] # Extra payload fields block_hash: Hash32 # Hash of execution block transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] + withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] ``` #### `ExecutionPayloadHeader` @@ -132,10 +135,11 @@ class ExecutionPayloadHeader(Container): timestamp: uint64 extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: uint256 - excess_blobs: uint64 # [New in EIP-4844] + excess_data_gas: uint256 # [New in EIP-4844] # Extra payload fields block_hash: Hash32 # Hash of execution block transactions_root: Root + withdrawals_root: Root ``` ## Helper functions @@ -227,7 +231,8 @@ def verify_kzg_commitments_against_transactions(transactions: Sequence[Transacti def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) + process_withdrawals(state, block.body.execution_payload) + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-4844] process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) @@ -253,6 +258,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid assert execution_engine.notify_new_payload(payload) + # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, @@ -267,9 +273,10 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe timestamp=payload.timestamp, extra_data=payload.extra_data, base_fee_per_gas=payload.base_fee_per_gas, - excess_blobs=payload.excess_blobs, # [New in EIP-4844] + excess_data_gas=payload.excess_data_gas, # [New in EIP-4844] block_hash=payload.block_hash, transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), ) ``` @@ -335,3 +342,10 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, return state ``` + +### Disabling Withdrawals +During testing we avoid Capella-specific updates to the state transition. We do this by replacing the following functions with a no-op implementation: +- `process_withdrawals` +- `process_bls_to_execution_change` + +The `get_expected_withdrawals` function is also modified to return an empty withdrawals list. As such, the PayloadAttributes used to update forkchoice does not contain withdrawals. diff --git a/specs/eip4844/fork.md b/specs/eip4844/fork.md index eaabba916..ea5187c41 100644 --- a/specs/eip4844/fork.md +++ b/specs/eip4844/fork.md @@ -44,6 +44,8 @@ def compute_fork_version(epoch: Epoch) -> Version: """ if epoch >= EIP4844_FORK_EPOCH: return EIP4844_FORK_VERSION + if epoch >= CAPELLA_FORK_EPOCH: + return CAPELLA_FORK_VERSION if epoch >= BELLATRIX_FORK_EPOCH: return BELLATRIX_FORK_VERSION if epoch >= ALTAIR_FORK_EPOCH: @@ -62,12 +64,29 @@ Note that for the pure EIP-4844 networks, we don't apply `upgrade_to_eip4844` si ### Upgrading the state -Since the `eip4844.BeaconState` format is equal to the `bellatrix.BeaconState` format, we only have to update `BeaconState.fork`. +Since the `eip4844.BeaconState` format is equal to the `capella.BeaconState` format, we only have to update `BeaconState.fork`. ```python -def upgrade_to_eip4844(pre: bellatrix.BeaconState) -> BeaconState: - # TODO: if Capella gets scheduled, add sync it with Capella.BeaconState - epoch = bellatrix.get_current_epoch(pre) +def upgrade_to_eip4844(pre: capella.BeaconState) -> BeaconState: + epoch = capella.get_current_epoch(pre) + latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=pre.latest_execution_payload_header.parent_hash, + fee_recipient=pre.latest_execution_payload_header.fee_recipient, + state_root=pre.latest_execution_payload_header.state_root, + receipts_root=pre.latest_execution_payload_header.receipts_root, + logs_bloom=pre.latest_execution_payload_header.logs_bloom, + prev_randao=pre.latest_execution_payload_header.prev_randao, + block_number=pre.latest_execution_payload_header.block_number, + gas_limit=pre.latest_execution_payload_header.gas_limit, + gas_used=pre.latest_execution_payload_header.gas_used, + timestamp=pre.latest_execution_payload_header.timestamp, + extra_data=pre.latest_execution_payload_header.extra_data, + base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas, + excess_data_gas=uint256(0), # [New in EIP-4844] + block_hash=pre.latest_execution_payload_header.block_hash, + transactions_root=pre.latest_execution_payload_header.transactions_root, + withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, + ) post = BeaconState( # Versioning genesis_time=pre.genesis_time, @@ -108,7 +127,10 @@ def upgrade_to_eip4844(pre: bellatrix.BeaconState) -> BeaconState: current_sync_committee=pre.current_sync_committee, next_sync_committee=pre.next_sync_committee, # Execution-layer - latest_execution_payload_header=pre.latest_execution_payload_header, + latest_execution_payload_header=latest_execution_payload_header, # [Modified in EIP4844] + # Withdrawals + next_withdrawal_index=pre.next_withdrawal_index, + next_withdrawal_validator_index=pre.next_withdrawal_validator_index, ) return post diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 03363e1b8..f031b3723 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -17,12 +17,14 @@ The specification of these changes continues in the same format as the network s - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) + - [`beacon_block`](#beacon_block) - [`beacon_block_and_blobs_sidecar`](#beacon_block_and_blobs_sidecar) - [Transitioning the gossip](#transitioning-the-gossip) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) - [BeaconBlocksByRange v2](#beaconblocksbyrange-v2) - [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2) + - [BeaconBlockAndBlobsSidecarByRoot v1](#beaconblockandblobssidecarbyroot-v1) - [BlobsSidecarsByRange v1](#blobssidecarsbyrange-v1) - [Design decision rationale](#design-decision-rationale) - [Why are blobs relayed as a sidecar, separate from beacon blocks?](#why-are-blobs-relayed-as-a-sidecar-separate-from-beacon-blocks) @@ -64,9 +66,9 @@ Some gossip meshes are upgraded in the fork of EIP4844 to support upgraded types ### Topics and messages Topics follow the same specification as in prior upgrades. -All topics remain stable except the beacon block topic which is updated with the modified type. +The `beacon_block` topic is deprecated and replaced by the `beacon_block_and_blobs_sidecar` topic. All other topics remain stable. -The specification around the creation, validation, and dissemination of messages has not changed from the Bellatrix document unless explicitly noted here. +The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here. The derivation of the `message-id` remains stable. @@ -81,11 +83,19 @@ The new topics along with the type of the `data` field of a gossipsub message ar EIP4844 introduces a new global topic for beacon block and blobs-sidecars. +##### `beacon_block` + +This topic is deprecated and clients **MUST NOT** expose in their topic set to any peer. Implementers do not need to do +anything beyond simply skip implementation, and it is explicitly called out as it is a departure from previous versioning +of this topic. + +Refer to [the section below](#transitioning-the-gossip) for details on how to transition the gossip. + ##### `beacon_block_and_blobs_sidecar` This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks. -In addition to the gossip validations for the `beacon_block` topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network. +In addition to the gossip validations for the `beacon_block` topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network. Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. - _[REJECT]_ The KZG commitments of the blobs are all correctly encoded compressed BLS G1 Points. -- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzg_commitments)` @@ -96,8 +106,8 @@ Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`. - _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `sidecar.beacon_block_slot == block.slot`. - _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`). - _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 Point -- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)` - -Once the sidecar and beacon block are received together, `validate_blobs_sidecar` can unlock the data-availability fork-choice dependency. +- _[REJECT]_ The KZG commitments in the block are valid against the provided blobs sidecar. + -- i.e. `validate_blobs_sidecar(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments, sidecar)` ### Transitioning the gossip @@ -123,13 +133,15 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: | `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | | `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | | `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` | +| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` | | `EIP4844_FORK_VERSION` | `eip4844.SignedBeaconBlock` | #### BeaconBlocksByRoot v2 **Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/` -The EIP-4844 fork-digest is introduced to the `context` enum to specify EIP-4844 beacon block type. +After `EIP4844_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1` +clients MUST support requesting blocks by root for pre-fork-epoch blocks. Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: @@ -140,7 +152,43 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: | `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | | `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | | `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` | -| `EIP4844_FORK_VERSION` | `eip4844.SignedBeaconBlock` | +| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` | + +#### BeaconBlockAndBlobsSidecarByRoot v1 + +**Protocol ID:** `/eth2/beacon_chain/req/beacon_block_and_blobs_sidecar_by_root/1/` + +Request Content: + +``` +( + List[Root, MAX_REQUEST_BLOCKS] +) +``` + +Response Content: + +``` +( + List[SignedBeaconBlockAndBlobsSidecar, MAX_REQUEST_BLOCKS] +) +``` + +Requests blocks by block root (= `hash_tree_root(SignedBeaconBlockAndBlobsSidecar.beacon_block.message)`). +The response is a list of `SignedBeaconBlockAndBlobsSidecar` whose length is less than or equal to the number of requests. +It may be less in the case that the responding peer is missing blocks and sidecars. + +No more than `MAX_REQUEST_BLOCKS` may be requested at a time. + +`BeaconBlockAndBlobsSidecarByRoot` is primarily used to recover recent blocks and sidecars (e.g. when receiving a block or attestation whose parent is unknown). + +The response MUST consist of zero or more `response_chunk`. +Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload. + +Clients MUST support requesting blocks and sidecars since the latest finalized epoch. + +Clients MUST respond with at least one block and sidecar, if they have it. +Clients MAY limit the number of blocks and sidecars in the response. #### BlobsSidecarsByRange v1 diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 2c142c04b..b945d317a 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -32,6 +32,7 @@ - [KZG](#kzg) - [`blob_to_kzg_commitment`](#blob_to_kzg_commitment) - [`verify_kzg_proof`](#verify_kzg_proof) + - [`verify_kzg_proof_impl`](#verify_kzg_proof_impl) - [`compute_kzg_proof`](#compute_kzg_proof) - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) - [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof) @@ -45,6 +46,8 @@ This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations. +Functions flagged as "Public method" MUST be provided by the underlying KZG library as public functions. All other functions are private functions used internally by the KZG library. + ## Custom types | Name | SSZ equivalent | Description | @@ -276,7 +279,7 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, result = 0 for i in range(width): - result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - roots_of_unity_brp[i])) + result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - int(roots_of_unity_brp[i]))) result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS return result ``` @@ -289,6 +292,9 @@ KZG core functions. These are also defined in EIP-4844 execution specs. ```python def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment: + """ + Public method. + """ return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob)) ``` @@ -296,11 +302,27 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment: ```python def verify_kzg_proof(polynomial_kzg: KZGCommitment, - z: BLSFieldElement, - y: BLSFieldElement, + z: Bytes32, + y: Bytes32, kzg_proof: KZGProof) -> bool: """ Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``. + Receives inputs as bytes. + Public method. + """ + return verify_kzg_proof_impl(polynomial_kzg, bytes_to_bls_field(z), bytes_to_bls_field(y), kzg_proof) +``` + + +#### `verify_kzg_proof_impl` + +```python +def verify_kzg_proof_impl(polynomial_kzg: KZGCommitment, + z: BLSFieldElement, + y: BLSFieldElement, + kzg_proof: KZGProof) -> bool: + """ + Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``. """ # Verify: P - y = Q * (X - z) X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2, BLS_MODULUS - z)) @@ -367,6 +389,9 @@ def compute_aggregated_poly_and_commitment( ```python def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof: + """ + Public method. + """ commitments = [blob_to_kzg_commitment(blob) for blob in blobs] aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( blobs, @@ -381,6 +406,9 @@ def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof: def verify_aggregate_kzg_proof(blobs: Sequence[Blob], expected_kzg_commitments: Sequence[KZGCommitment], kzg_aggregated_proof: KZGCommitment) -> bool: + """ + Public method. + """ aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( blobs, expected_kzg_commitments, @@ -390,5 +418,5 @@ def verify_aggregate_kzg_proof(blobs: Sequence[Blob], y = evaluate_polynomial_in_evaluation_form(aggregated_poly, evaluation_challenge) # Verify aggregated proof - return verify_kzg_proof(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof) + return verify_kzg_proof_impl(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof) ``` diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index d03b1842b..57a5610ce 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -29,7 +29,7 @@ This document represents the changes to be made in the code of an "honest valida ## Prerequisites -This document is an extension of the [Bellatrix -- Honest Validator](../bellatrix/validator.md) guide. +This document is an extension of the [Capella -- Honest Validator](../capella/validator.md) guide. All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. All terminology, constants, functions, and protocol mechanics defined in the updated [Beacon Chain doc of EIP4844](./beacon-chain.md) are requisite for this document and used throughout. @@ -60,7 +60,7 @@ Namely, the blob handling and the addition of `SignedBeaconBlockAndBlobsSidecar` ##### Blob KZG commitments -1. After retrieving the execution payload from the execution engine as specified in Bellatrix, +1. After retrieving the execution payload from the execution engine as specified in Capella, use the `payload_id` to retrieve `blobs` and `blob_kzg_commitments` via `get_blobs_and_kzg_commitments(payload_id)`. 2. Validate `blobs` and `blob_kzg_commitments`: diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 000e3207c..94dbaa2cf 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.3.0-alpha.0 +1.3.0-alpha.1 diff --git a/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py b/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py index dccb9313f..0a831a592 100644 --- a/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py +++ b/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py @@ -4,7 +4,6 @@ import time import shutil import argparse from pathlib import Path -from filelock import FileLock import sys import json from typing import Iterable, AnyStr, Any, Callable @@ -13,6 +12,7 @@ from ruamel.yaml import ( YAML, ) +from filelock import FileLock from snappy import compress from eth2spec.test import context @@ -141,6 +141,10 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]): tprov.prepare() for test_case in tprov.make_cases(): + # If preset list is assigned, filter by presets. + if len(presets) != 0 and test_case.preset_name not in presets: + continue + case_dir = ( Path(output_dir) / Path(test_case.preset_name) / Path(test_case.fork_name) / Path(test_case.runner_name) / Path(test_case.handler_name) @@ -179,7 +183,16 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]): try: fn(case_dir) except IOError as e: - sys.exit(f'Error when dumping test "{case_dir}", part "{name}", kind "{out_kind}": {e}') + error_message = ( + f'[Error] error when dumping test "{case_dir}", part "{name}", kind "{out_kind}": {e}' + ) + # Write to error log file + with log_file.open("a+") as f: + f.write(error_message) + traceback.print_exc(file=f) + f.write('\n') + + sys.exit(error_message) meta = dict() @@ -210,13 +223,13 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]): if not written_part: print(f"test case {case_dir} did not produce any test case parts") except Exception as e: - print(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}") - traceback.print_exc() - # Write to log file + error_message = f"[ERROR] failed to generate vector(s) for test {case_dir}: {e}" + # Write to error log file with log_file.open("a+") as f: - f.write(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}") + f.write(error_message) traceback.print_exc(file=f) f.write('\n') + traceback.print_exc() else: # If no written_part, the only file was incomplete_tag_file. Clear the existing case_dir folder. if not written_part: diff --git a/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py b/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py index 328b7edf1..b951a6a85 100644 --- a/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py +++ b/tests/core/pyspec/eth2spec/gen_helpers/gen_from_tests/gen.py @@ -49,7 +49,7 @@ def generate_from_tests(runner_name: str, handler_name: str, src: Any, preset_name=preset_name, runner_name=runner_name, handler_name=handler_name, - suite_name='pyspec_tests', + suite_name=getattr(tfn, 'suite_name', 'pyspec_tests'), case_name=case_name, # TODO: with_all_phases and other per-phase tooling, should be replaced with per-fork equivalent. case_fn=lambda: tfn(generator_mode=True, phase=phase, preset=preset_name, bls_active=bls_active) diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py index 064760bf0..465fa629f 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_single_merkle_proof.py @@ -1,14 +1,17 @@ from eth2spec.test.context import ( spec_state_test, with_altair_and_later, + with_test_suite_name, ) +@with_test_suite_name("BeaconState") @with_altair_and_later @spec_state_test def test_current_sync_committee_merkle_proof(spec, state): - yield "state", state - current_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX) + yield "object", state + current_sync_committee_branch = \ + spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX) yield "proof", { "leaf": "0x" + state.current_sync_committee.hash_tree_root().hex(), "leaf_index": spec.CURRENT_SYNC_COMMITTEE_INDEX, @@ -23,11 +26,13 @@ def test_current_sync_committee_merkle_proof(spec, state): ) +@with_test_suite_name("BeaconState") @with_altair_and_later @spec_state_test def test_next_sync_committee_merkle_proof(spec, state): - yield "state", state - next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) + yield "object", state + next_sync_committee_branch = \ + spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) yield "proof", { "leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(), "leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX, @@ -42,11 +47,13 @@ def test_next_sync_committee_merkle_proof(spec, state): ) +@with_test_suite_name("BeaconState") @with_altair_and_later @spec_state_test def test_finality_root_merkle_proof(spec, state): - yield "state", state - finality_branch = spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + yield "object", state + finality_branch = \ + spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) yield "proof", { "leaf": "0x" + state.finalized_checkpoint.root.hex(), "leaf_index": spec.FINALIZED_ROOT_INDEX, diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py index 8ff02489c..79d79fa6e 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_bls_to_execution_change.py @@ -1,7 +1,8 @@ +from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change -from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_phases, always_bls def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True): @@ -37,14 +38,14 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v yield 'post', state -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success(spec, state): signed_address_change = get_signed_address_change(spec, state) yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_not_activated(spec, state): validator_index = 3 @@ -62,7 +63,7 @@ def test_success_not_activated(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_in_activation_queue(spec, state): validator_index = 3 @@ -80,7 +81,7 @@ def test_success_in_activation_queue(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_in_exit_queue(spec, state): validator_index = 3 @@ -93,7 +94,7 @@ def test_success_in_exit_queue(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_exited(spec, state): validator_index = 4 @@ -110,7 +111,7 @@ def test_success_exited(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_withdrawable(spec, state): validator_index = 4 @@ -128,7 +129,7 @@ def test_success_withdrawable(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_val_index_out_of_range(spec, state): # Create for one validator beyond the validator list length @@ -137,7 +138,7 @@ def test_fail_val_index_out_of_range(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_already_0x01(spec, state): # Create for one validator beyond the validator list length @@ -149,7 +150,7 @@ def test_fail_already_0x01(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_from_bls_pubkey(spec, state): # Create for one validator beyond the validator list length @@ -163,7 +164,7 @@ def test_fail_incorrect_from_bls_pubkey(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test @always_bls def test_fail_bad_signature(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py index e0603d301..685d17651 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_deposit.py @@ -1,7 +1,8 @@ from eth2spec.test.context import ( spec_state_test, - with_capella_and_later, + with_phases, ) +from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.state import next_epoch_via_block from eth2spec.test.helpers.deposits import ( prepare_state_and_deposit, @@ -10,7 +11,7 @@ from eth2spec.test.helpers.deposits import ( from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_top_up_to_withdrawn_validator(spec, state): validator_index = 0 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 7b39f2b9d..da3ddcb4d 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -3,10 +3,10 @@ import random from eth2spec.test.context import ( spec_state_test, expect_assertion_error, - with_capella_and_later, with_presets, + with_phases, ) -from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.constants import MINIMAL, CAPELLA from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, ) @@ -87,7 +87,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with return expected_withdrawals -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_zero_expected_withdrawals(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 @@ -98,7 +98,7 @@ def test_success_zero_expected_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_full_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -115,7 +115,7 @@ def test_success_one_full_withdrawal(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -135,7 +135,7 @@ def test_success_one_partial_withdrawal(spec, state): ) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_max_per_slot(spec, state): num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2 @@ -153,7 +153,7 @@ def test_success_max_per_slot(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_all_fully_withdrawable(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -168,7 +168,7 @@ def test_success_all_fully_withdrawable(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_all_partially_withdrawable(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -187,7 +187,7 @@ def test_success_all_partially_withdrawable(spec, state): # Failure cases in which the number of withdrawals in the execution_payload is incorrect # -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_non_withdrawable_non_empty_withdrawals(spec, state): next_slot(spec, state) @@ -203,7 +203,7 @@ def test_fail_non_withdrawable_non_empty_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -215,7 +215,7 @@ def test_fail_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -227,7 +227,7 @@ def test_fail_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, stat yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=2) @@ -239,7 +239,7 @@ def test_fail_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, st yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=2) @@ -251,7 +251,7 @@ def test_fail_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) @@ -263,7 +263,7 @@ def test_fail_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, st yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) @@ -275,7 +275,7 @@ def test_fail_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -287,7 +287,7 @@ def test_fail_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -299,7 +299,7 @@ def test_fail_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4, @@ -316,7 +316,7 @@ def test_fail_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, sta # Failure cases in which the withdrawals in the execution_payload are incorrect # -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_withdrawal_index(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -328,7 +328,7 @@ def test_fail_incorrect_withdrawal_index(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_address_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -340,7 +340,7 @@ def test_fail_incorrect_address_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_address_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -352,7 +352,7 @@ def test_fail_incorrect_address_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_amount_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -364,7 +364,7 @@ def test_fail_incorrect_amount_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_incorrect_amount_partial(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -376,7 +376,7 @@ def test_fail_incorrect_amount_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_one_of_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -394,7 +394,7 @@ def test_fail_one_of_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_one_of_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -412,7 +412,7 @@ def test_fail_one_of_many_incorrectly_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -430,7 +430,7 @@ def test_fail_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_fail_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -452,7 +452,7 @@ def test_fail_many_incorrectly_partial(spec, state): # More full withdrawal cases # -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_withdrawable_epoch_but_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -466,7 +466,7 @@ def test_withdrawable_epoch_but_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -480,7 +480,7 @@ def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -494,7 +494,7 @@ def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state) yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_no_withdrawals_but_some_next_epoch(spec, state): current_epoch = spec.get_current_epoch(state) @@ -508,7 +508,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_all_withdrawal(spec, state): # Make all validators withdrawable @@ -544,25 +544,25 @@ def run_random_full_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_full_withdrawals_0(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(444)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_full_withdrawals_1(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(420)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_full_withdrawals_2(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(200)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_full_withdrawals_3(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000)) @@ -572,7 +572,7 @@ def test_random_full_withdrawals_3(spec, state): # More partial withdrawal cases # -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -588,7 +588,7 @@ def test_success_no_max_effective_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_no_excess_balance(spec, state): validator_index = len(state.validators) // 2 @@ -604,7 +604,7 @@ def test_success_no_excess_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_excess_balance_but_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -621,7 +621,7 @@ def test_success_excess_balance_but_no_max_effective_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawable_not_yet_active(spec, state): validator_index = len(state.validators) // 2 @@ -635,7 +635,7 @@ def test_success_one_partial_withdrawable_not_yet_active(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawable_in_exit_queue(spec, state): validator_index = len(state.validators) // 2 @@ -650,7 +650,7 @@ def test_success_one_partial_withdrawable_in_exit_queue(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawable_exited(spec, state): validator_index = len(state.validators) // 2 @@ -664,7 +664,7 @@ def test_success_one_partial_withdrawable_exited(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawable_active_and_slashed(spec, state): validator_index = len(state.validators) // 2 @@ -678,7 +678,7 @@ def test_success_one_partial_withdrawable_active_and_slashed(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_one_partial_withdrawable_exited_and_slashed(spec, state): validator_index = len(state.validators) // 2 @@ -693,7 +693,7 @@ def test_success_one_partial_withdrawable_exited_and_slashed(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_two_partial_withdrawable(spec, state): set_validator_partially_withdrawable(spec, state, 0) @@ -704,7 +704,7 @@ def test_success_two_partial_withdrawable(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=2) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_success_max_partial_withdrawable(spec, state): # Sanity check that this test works for this state @@ -719,7 +719,7 @@ def test_success_max_partial_withdrawable(spec, state): spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) -@with_capella_and_later +@with_phases([CAPELLA]) @with_presets([MINIMAL], reason="not enough validators with mainnet config") @spec_state_test def test_success_max_plus_one_withdrawable(spec, state): @@ -758,37 +758,37 @@ def run_random_partial_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_0(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(0)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_partial_withdrawals_1(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(1)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_partial_withdrawals_2(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(2)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_partial_withdrawals_3(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(3)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_partial_withdrawals_4(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(4)) -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_random_partial_withdrawals_5(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(5)) diff --git a/tests/core/pyspec/eth2spec/test/capella/fork/test_capella_fork_random.py b/tests/core/pyspec/eth2spec/test/capella/fork/test_capella_fork_random.py index e69de29bb..87b4667e0 100644 --- a/tests/core/pyspec/eth2spec/test/capella/fork/test_capella_fork_random.py +++ b/tests/core/pyspec/eth2spec/test/capella/fork/test_capella_fork_random.py @@ -0,0 +1,84 @@ +from random import Random + +from eth2spec.test.context import ( + with_phases, + with_custom_state, + with_presets, + spec_test, with_state, + low_balances, misc_balances, large_validator_set, +) +from eth2spec.test.utils import with_meta_tags +from eth2spec.test.helpers.constants import ( + BELLATRIX, CAPELLA, + MINIMAL, +) +from eth2spec.test.helpers.capella.fork import ( + CAPELLA_FORK_TEST_META_TAGS, + run_fork_test, +) +from eth2spec.test.helpers.random import randomize_state + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_state +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_0(spec, phases, state): + randomize_state(spec, state, rng=Random(1010)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_state +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_1(spec, phases, state): + randomize_state(spec, state, rng=Random(2020)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_state +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_2(spec, phases, state): + randomize_state(spec, state, rng=Random(3030)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_state +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_3(spec, phases, state): + randomize_state(spec, state, rng=Random(4040)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_low_balances(spec, phases, state): + randomize_state(spec, state, rng=Random(5050)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@spec_test +@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_misc_balances(spec, phases, state): + randomize_state(spec, state, rng=Random(6060)) + yield from run_fork_test(phases[CAPELLA], state) + + +@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA]) +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@spec_test +@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(CAPELLA_FORK_TEST_META_TAGS) +def test_capella_fork_random_large_validator_set(spec, phases, state): + randomize_state(spec, state, rng=Random(7070)) + yield from run_fork_test(phases[CAPELLA], state) diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py index f3ad843b1..229d8ecc5 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -1,7 +1,7 @@ from eth2spec.test.context import ( - with_capella_and_later, spec_state_test + with_phases, spec_state_test ) - +from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) @@ -21,9 +21,13 @@ from eth2spec.test.helpers.withdrawals import ( from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits -@with_capella_and_later +# +# BLSToExecutionChange +# + +@with_phases([CAPELLA]) @spec_state_test -def test_successful_bls_change(spec, state): +def test_success_bls_change(spec, state): index = 0 signed_address_change = get_signed_address_change(spec, state, validator_index=index) pre_credentials = state.validators[index].withdrawal_credentials @@ -44,77 +48,9 @@ def test_successful_bls_change(spec, state): assert post_credentials[12:] == signed_address_change.message.to_execution_address -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test -def test_full_withdrawal_in_epoch_transition(spec, state): - index = 0 - current_epoch = spec.get_current_epoch(state) - set_validator_fully_withdrawable(spec, state, index, current_epoch) - assert len(spec.get_expected_withdrawals(state)) == 1 - - yield 'pre', state - - # trigger epoch transition - block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - assert state.balances[index] == 0 - assert len(spec.get_expected_withdrawals(state)) == 0 - - -@with_capella_and_later -@spec_state_test -def test_partial_withdrawal_in_epoch_transition(spec, state): - index = state.next_withdrawal_index - set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) - pre_balance = state.balances[index] - - assert len(spec.get_expected_withdrawals(state)) == 1 - - yield 'pre', state - - # trigger epoch transition - block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - assert state.balances[index] < pre_balance - # Potentially less than due to sync committee penalty - assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE - assert len(spec.get_expected_withdrawals(state)) == 0 - - -@with_capella_and_later -@spec_state_test -def test_many_partial_withdrawals_in_epoch_transition(spec, state): - assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD - - for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1): - index = (i + state.next_withdrawal_index) % len(state.validators) - set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) - - assert len(spec.get_expected_withdrawals(state)) == spec.MAX_WITHDRAWALS_PER_PAYLOAD - - yield 'pre', state - - # trigger epoch transition - block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - assert len(spec.get_expected_withdrawals(state)) == 1 - - -@with_capella_and_later -@spec_state_test -def test_exit_and_bls_change(spec, state): +def test_success_exit_and_bls_change(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH @@ -141,6 +77,121 @@ def test_exit_and_bls_change(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch) +@with_phases([CAPELLA]) +@spec_state_test +def test_invalid_duplicate_bls_changes_same_block(spec, state): + index = 0 + signed_address_change = get_signed_address_change(spec, state, validator_index=index) + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + # Double BLSToExecutionChange of the same validator + for _ in range(2): + block.body.bls_to_execution_changes.append(signed_address_change) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + +@with_phases([CAPELLA]) +@spec_state_test +def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_block(spec, state): + index = 0 + + signed_address_change_1 = get_signed_address_change(spec, state, validator_index=index, + to_execution_address=b'\x12' * 20) + signed_address_change_2 = get_signed_address_change(spec, state, validator_index=index, + to_execution_address=b'\x34' * 20) + assert signed_address_change_1 != signed_address_change_2 + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + block.body.bls_to_execution_changes.append(signed_address_change_1) + block.body.bls_to_execution_changes.append(signed_address_change_2) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + +# +# Withdrawals +# + +@with_phases([CAPELLA]) +@spec_state_test +def test_full_withdrawal_in_epoch_transition(spec, state): + index = 0 + current_epoch = spec.get_current_epoch(state) + set_validator_fully_withdrawable(spec, state, index, current_epoch) + assert len(spec.get_expected_withdrawals(state)) == 1 + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] == 0 + assert len(spec.get_expected_withdrawals(state)) == 0 + + +@with_phases([CAPELLA]) +@spec_state_test +def test_partial_withdrawal_in_epoch_transition(spec, state): + index = state.next_withdrawal_index + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + pre_balance = state.balances[index] + + assert len(spec.get_expected_withdrawals(state)) == 1 + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert state.balances[index] < pre_balance + # Potentially less than due to sync committee penalty + assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE + assert len(spec.get_expected_withdrawals(state)) == 0 + + +@with_phases([CAPELLA]) +@spec_state_test +def test_many_partial_withdrawals_in_epoch_transition(spec, state): + assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD + + for i in range(spec.MAX_WITHDRAWALS_PER_PAYLOAD + 1): + index = (i + state.next_withdrawal_index) % len(state.validators) + set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000) + + assert len(spec.get_expected_withdrawals(state)) == spec.MAX_WITHDRAWALS_PER_PAYLOAD + + yield 'pre', state + + # trigger epoch transition + block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_block = state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [signed_block] + yield 'post', state + + assert len(spec.get_expected_withdrawals(state)) == 1 + + def _perform_valid_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4, @@ -170,7 +221,7 @@ def _perform_valid_withdrawal(spec, state): return pre_state, signed_block_1, pre_next_withdrawal_index -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_withdrawal_success_two_blocks(spec, state): pre_state, signed_block_1, pre_next_withdrawal_index = _perform_valid_withdrawal(spec, state) @@ -187,7 +238,7 @@ def test_withdrawal_success_two_blocks(spec, state): yield 'post', state -@with_capella_and_later +@with_phases([CAPELLA]) @spec_state_test def test_withdrawal_fail_second_block_payload_isnt_compatible(spec, state): _perform_valid_withdrawal(spec, state) diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 80aed92f2..a7844abd4 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -600,6 +600,13 @@ def only_generator(reason): return _decorator +def with_test_suite_name(suite_name: str): + def _decorator(inner): + inner.suite_name = suite_name + return inner + return _decorator + + # # Fork transition state tests # diff --git a/tests/core/pyspec/eth2spec/test/eip4844/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py new file mode 100644 index 000000000..d9b93394f --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py @@ -0,0 +1,40 @@ +from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later + + +def run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change, valid=True): + """ + Run ``process_bls_to_execution_change``, yielding: + - pre-state ('pre') + - address-change ('address_change') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + pre_state = state.copy() + + # yield pre-state + yield 'pre', state + + yield 'address_change', signed_address_change + + # If the address_change is invalid, processing is aborted, and there is no post-state. + if not valid: + expect_assertion_error(lambda: spec.process_bls_to_execution_change(state, signed_address_change)) + yield 'post', None + return + + # process address change + spec.process_bls_to_execution_change(state, signed_address_change) + + # yield post-state + yield 'post', state + + # Make sure state has NOT been changed + assert state == pre_state + + +@with_eip4844_and_later +@spec_state_test +def test_no_op(spec, state): + signed_address_change = get_signed_address_change(spec, state) + yield from run_bls_to_execution_change_processing_no_op(spec, state, signed_address_change) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py new file mode 100644 index 000000000..a7db37e42 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py @@ -0,0 +1,41 @@ + +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_eip4844_and_later +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, +) +from eth2spec.test.helpers.state import next_slot + + +def run_withdrawals_processing(spec, state, execution_payload, valid=True): + """ + Run ``process_execution_payload``, yielding: + - pre-state ('pre') + - execution payload ('execution_payload') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + pre_state = state.copy() + + yield 'pre', state + yield 'execution_payload', execution_payload + + if not valid: + expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload)) + yield 'post', None + return + + spec.process_withdrawals(state, execution_payload) + + yield 'post', state + + # Make sure state has NOT been changed + assert state == pre_state + + +@with_eip4844_and_later +@spec_state_test +def test_no_op(spec, state): + next_slot(spec, state) + execution_payload = build_empty_execution_payload(spec, state) + + yield from run_withdrawals_processing(spec, state, execution_payload) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/fork/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/fork/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_basic.py b/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_basic.py new file mode 100644 index 000000000..aca7cb852 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_basic.py @@ -0,0 +1,82 @@ +from eth2spec.test.context import ( + with_phases, + with_custom_state, + with_presets, + spec_test, with_state, + low_balances, misc_balances, large_validator_set, +) +from eth2spec.test.utils import with_meta_tags +from eth2spec.test.helpers.constants import ( + CAPELLA, EIP4844, + MINIMAL, +) +from eth2spec.test.helpers.state import ( + next_epoch, + next_epoch_via_block, +) +from eth2spec.test.helpers.eip4844.fork import ( + EIP4844_FORK_TEST_META_TAGS, + run_fork_test, +) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_base_state(spec, phases, state): + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_next_epoch(spec, phases, state): + next_epoch(spec, state) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_next_epoch_with_block(spec, phases, state): + next_epoch_via_block(spec, state) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_many_next_epoch(spec, phases, state): + for _ in range(3): + next_epoch(spec, state) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@spec_test +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_random_low_balances(spec, phases, state): + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@spec_test +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_random_misc_balances(spec, phases, state): + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@spec_test +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_fork_random_large_validator_set(spec, phases, state): + yield from run_fork_test(phases[EIP4844], state) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_random.py b/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_random.py new file mode 100644 index 000000000..a22de4b59 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/fork/test_eip4844_fork_random.py @@ -0,0 +1,84 @@ +from random import Random + +from eth2spec.test.context import ( + with_phases, + with_custom_state, + with_presets, + spec_test, with_state, + low_balances, misc_balances, large_validator_set, +) +from eth2spec.test.utils import with_meta_tags +from eth2spec.test.helpers.constants import ( + CAPELLA, EIP4844, + MINIMAL, +) +from eth2spec.test.helpers.eip4844.fork import ( + EIP4844_FORK_TEST_META_TAGS, + run_fork_test, +) +from eth2spec.test.helpers.random import randomize_state + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_0(spec, phases, state): + randomize_state(spec, state, rng=Random(1010)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_1(spec, phases, state): + randomize_state(spec, state, rng=Random(2020)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_2(spec, phases, state): + randomize_state(spec, state, rng=Random(3030)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_state +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_3(spec, phases, state): + randomize_state(spec, state, rng=Random(4040)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_low_balances(spec, phases, state): + randomize_state(spec, state, rng=Random(5050)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@spec_test +@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_misc_balances(spec, phases, state): + randomize_state(spec, state, rng=Random(6060)) + yield from run_fork_test(phases[EIP4844], state) + + +@with_phases(phases=[CAPELLA], other_phases=[EIP4844]) +@with_presets([MINIMAL], + reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") +@spec_test +@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) +@with_meta_tags(EIP4844_FORK_TEST_META_TAGS) +def test_eip4844_fork_random_large_validator_set(spec, phases, state): + randomize_state(spec, state, rng=Random(7070)) + yield from run_fork_test(phases[EIP4844], state) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/random/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/random/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip4844/random/test_random.py b/tests/core/pyspec/eth2spec/test/eip4844/random/test_random.py new file mode 100644 index 000000000..b90b858b2 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/random/test_random.py @@ -0,0 +1,438 @@ +""" +This module is generated from the ``random`` test generator. +Please do not edit this file manually. +See the README for that generator for more information. +""" + +from eth2spec.test.helpers.constants import EIP4844 +from eth2spec.test.context import ( + misc_balances_in_default_range_with_many_validators, + with_phases, + zero_activation_threshold, + only_generator, +) +from eth2spec.test.context import ( + always_bls, + spec_test, + with_custom_state, + single_phase, +) +from eth2spec.test.utils.randomized_block_tests import ( + run_generated_randomized_test, +) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_0(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_1(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_2(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_3(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_4(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_5(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_6(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_7(spec, state): + # scenario as high-level, informal text: + # epochs:0,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'validation': 'validate_is_not_leaking', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_8(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_9(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_10(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_11(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_12(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:last_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'last_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_13(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:random_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'random_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_14(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:penultimate_slot_in_epoch,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 0, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 'penultimate_slot_in_epoch', 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) + + +@only_generator("randomized test for broad coverage, not point-to-point CI") +@with_phases([EIP4844]) +@with_custom_state( + balances_fn=misc_balances_in_default_range_with_many_validators, + threshold_fn=zero_activation_threshold +) +@spec_test +@single_phase +@always_bls +def test_randomized_15(spec, state): + # scenario as high-level, informal text: + # epochs:epochs_until_leak,slots:0,with-block:no_block + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + # epochs:1,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:no_block + # epochs:0,slots:0,with-block:random_block_eip4844 + scenario = {'transitions': [{'epochs_to_skip': 'epochs_until_leak', 'validation': 'validate_is_leaking', 'slots_to_skip': 0, 'block_producer': 'no_block'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}, {'epochs_to_skip': 1, 'slots_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'slots_to_skip': 0, 'epochs_to_skip': 0, 'block_producer': 'no_block', 'validation': 'no_op_validation'}, {'block_producer': 'random_block_eip4844', 'epochs_to_skip': 0, 'slots_to_skip': 0, 'validation': 'no_op_validation'}], 'state_randomizer': 'randomize_state_eip4844'} # noqa: E501 + yield from run_generated_randomized_test( + spec, + state, + scenario, + ) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py index dea6aeb8c..3e9e2cb63 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -17,4 +17,4 @@ def test_verify_kzg_proof(spec, state): proof = spec.compute_kzg_proof(polynomial, x) y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) - assert spec.verify_kzg_proof(commitment, x, y, proof) + assert spec.verify_kzg_proof_impl(commitment, x, y, proof) diff --git a/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py index 61c84b515..446fa9710 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py +++ b/tests/core/pyspec/eth2spec/test/helpers/bls_to_execution_changes.py @@ -2,7 +2,7 @@ from eth2spec.utils import bls from eth2spec.test.helpers.keys import pubkeys, privkeys, pubkey_to_privkey -def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None): +def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubkey=None, to_execution_address=None): if validator_index is None: validator_index = 0 @@ -13,11 +13,14 @@ def get_signed_address_change(spec, state, validator_index=None, withdrawal_pubk else: withdrawal_privkey = pubkey_to_privkey[withdrawal_pubkey] + if to_execution_address is None: + to_execution_address = b'\x42' * 20 + domain = spec.get_domain(state, spec.DOMAIN_BLS_TO_EXECUTION_CHANGE) address_change = spec.BLSToExecutionChange( validator_index=validator_index, from_bls_pubkey=withdrawal_pubkey, - to_execution_address=b'\x42' * 20, + to_execution_address=to_execution_address, ) signing_root = spec.compute_signing_root(address_change, domain) diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 598b189ef..b67b11f10 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -32,6 +32,7 @@ ALL_FORK_UPGRADES = { PHASE0: ALTAIR, ALTAIR: BELLATRIX, BELLATRIX: CAPELLA, + CAPELLA: EIP4844, } ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items() AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0} diff --git a/tests/core/pyspec/eth2spec/test/helpers/eip4844/__init__.py b/tests/core/pyspec/eth2spec/test/helpers/eip4844/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/helpers/eip4844/fork.py b/tests/core/pyspec/eth2spec/test/helpers/eip4844/fork.py new file mode 100644 index 000000000..ed4ae057d --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/helpers/eip4844/fork.py @@ -0,0 +1,63 @@ +from eth2spec.test.helpers.constants import ( + EIP4844, +) + + +EIP4844_FORK_TEST_META_TAGS = { + 'fork': EIP4844, +} + + +def run_fork_test(post_spec, pre_state): + yield 'pre', pre_state + + post_state = post_spec.upgrade_to_eip4844(pre_state) + + # Stable fields + stable_fields = [ + 'genesis_time', 'genesis_validators_root', 'slot', + # History + 'latest_block_header', 'block_roots', 'state_roots', 'historical_roots', + # Eth1 + 'eth1_data', 'eth1_data_votes', 'eth1_deposit_index', + # Registry + 'validators', 'balances', + # Randomness + 'randao_mixes', + # Slashings + 'slashings', + # Participation + 'previous_epoch_participation', 'current_epoch_participation', + # Finality + 'justification_bits', 'previous_justified_checkpoint', 'current_justified_checkpoint', 'finalized_checkpoint', + # Inactivity + 'inactivity_scores', + # Sync + 'current_sync_committee', 'next_sync_committee', + # Withdrawals + 'next_withdrawal_index', 'next_withdrawal_validator_index', + ] + for field in stable_fields: + assert getattr(pre_state, field) == getattr(post_state, field) + + # Modified fields + modified_fields = ['fork', 'latest_execution_payload_header'] + for field in modified_fields: + assert getattr(pre_state, field) != getattr(post_state, field) + + assert len(pre_state.validators) == len(post_state.validators) + for pre_validator, post_validator in zip(pre_state.validators, post_state.validators): + stable_validator_fields = [ + 'pubkey', 'withdrawal_credentials', + 'effective_balance', + 'slashed', + 'activation_eligibility_epoch', 'activation_epoch', 'exit_epoch', 'withdrawable_epoch', + ] + for field in stable_validator_fields: + assert getattr(pre_validator, field) == getattr(post_validator, field) + + assert pre_state.fork.current_version == post_state.fork.previous_version + assert post_state.fork.current_version == post_spec.config.EIP4844_FORK_VERSION + assert post_state.fork.epoch == post_spec.get_current_epoch(post_state) + + yield 'post', post_state diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index d6d88876a..82ff12ff1 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -5,7 +5,7 @@ from .constants import ( def is_post_fork(a, b): if a == EIP4844: - return b in [PHASE0, ALTAIR, BELLATRIX, EIP4844] + return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844] if a == CAPELLA: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA] if a == BELLATRIX: diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index b94a01f7f..0e19e65b7 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -433,7 +433,7 @@ def test_proposer_slashing(spec, state): @with_all_phases @spec_state_test -def test_double_same_proposer_slashings_same_block(spec, state): +def test_invalid_duplicate_proposer_slashings_same_block(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) slashed_index = proposer_slashing.signed_header_1.message.proposer_index assert not state.validators[slashed_index].slashed @@ -450,7 +450,7 @@ def test_double_same_proposer_slashings_same_block(spec, state): @with_all_phases @spec_state_test -def test_double_similar_proposer_slashings_same_block(spec, state): +def test_invalid_similar_proposer_slashings_same_block(spec, state): slashed_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] # Same validator, but different slashable offences in the same block @@ -549,7 +549,7 @@ def test_attester_slashing(spec, state): @with_all_phases @spec_state_test -def test_duplicate_attester_slashing(spec, state): +def test_invalid_duplicate_attester_slashing_same_block(spec, state): if spec.MAX_ATTESTER_SLASHINGS < 2: return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block") @@ -744,6 +744,27 @@ def test_deposit_in_block(spec, state): assert state.validators[validator_index].pubkey == pubkeys[validator_index] +@with_all_phases +@spec_state_test +def test_invalid_duplicate_deposit_same_block(spec, state): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + + # The same deposit of the same validator + for _ in range(2): + block.body.deposits.append(deposit) + + signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + + yield 'blocks', [signed_block] + yield 'post', None + + @with_all_phases @spec_state_test def test_deposit_top_up(spec, state): @@ -831,6 +852,49 @@ def test_attestation(spec, state): assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root +@with_all_phases +@spec_state_test +def test_duplicate_attestation_same_block(spec, state): + next_epoch(spec, state) + + yield 'pre', state + + attestation_block = build_empty_block(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) + + index = 0 + + attestation = get_valid_attestation(spec, state, index=index, signed=True) + + if not is_post_altair(spec): + pre_current_attestations_len = len(state.current_epoch_attestations) + + # Add to state via block transition + for _ in range(2): + attestation_block.body.attestations.append(attestation) + signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block) + + if not is_post_altair(spec): + assert len(state.current_epoch_attestations) == pre_current_attestations_len + 2 + # Epoch transition should move to previous_epoch_attestations + pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations) + else: + pre_current_epoch_participation_root = spec.hash_tree_root(state.current_epoch_participation) + + epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) + signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block) + + yield 'blocks', [signed_attestation_block, signed_epoch_block] + yield 'post', state + + if not is_post_altair(spec): + assert len(state.current_epoch_attestations) == 0 + assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root + else: + for index in range(len(state.validators)): + assert state.current_epoch_participation[index] == spec.ParticipationFlags(0b0000_0000) + assert spec.hash_tree_root(state.previous_epoch_participation) == pre_current_epoch_participation_root + + # After SHARDING is enabled, a committee is computed for SHARD_COMMITTEE_PERIOD slots ago, # exceeding the minimal-config randao mixes memory size. # Applies to all voluntary-exit sanity block tests. @@ -866,7 +930,7 @@ def test_voluntary_exit(spec, state): @with_all_phases @spec_state_test -def test_double_validator_exit_same_block(spec, state): +def test_invalid_duplicate_validator_exit_same_block(spec, state): validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit diff --git a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py index 5504a53d7..37fbdc417 100644 --- a/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py +++ b/tests/core/pyspec/eth2spec/test/utils/randomized_block_tests.py @@ -20,6 +20,9 @@ from eth2spec.test.helpers.random import ( randomize_state as randomize_state_helper, patch_state_to_non_leaking, ) +from eth2spec.test.helpers.sharding import ( + get_sample_opaque_tx, +) from eth2spec.test.helpers.state import ( next_slot, next_epoch, @@ -78,6 +81,17 @@ def randomize_state_capella(spec, state, stats, exit_fraction=0.1, slash_fractio stats, exit_fraction=exit_fraction, slash_fraction=slash_fraction) + # TODO: randomize withdrawals + return scenario_state + + +def randomize_state_eip4844(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1): + scenario_state = randomize_state_capella(spec, + state, + stats, + exit_fraction=exit_fraction, + slash_fraction=slash_fraction) + # TODO: randomize execution payload return scenario_state @@ -215,6 +229,16 @@ def random_block_capella(spec, state, signed_blocks, scenario_state, rng=Random( return block +def random_block_eip4844(spec, state, signed_blocks, scenario_state, rng=Random(3456)): + block = random_block_capella(spec, state, signed_blocks, scenario_state) + # TODO: more commitments. blob_kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] + opaque_tx, _, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=1) + block.body.execution_payload.transactions = [opaque_tx] + block.body.blob_kzg_commitments = blob_kzg_commitments + + return block + + # validations def no_op_validation(_spec, _state): diff --git a/tests/formats/light_client/single_merkle_proof.md b/tests/formats/light_client/single_merkle_proof.md index 65fe7c988..d2137605e 100644 --- a/tests/formats/light_client/single_merkle_proof.md +++ b/tests/formats/light_client/single_merkle_proof.md @@ -5,24 +5,26 @@ generation and verification of merkle proofs based on static data. ## Test case format -### `state.ssz_snappy` +Tests for each individual SSZ type are grouped into a `suite` indicating the SSZ type name. -An SSZ-snappy encoded `BeaconState` object from which other data is generated. +### `object.yaml` + +A SSZ-snappy encoded object from which other data is generated. The SSZ type can be determined from the test `suite` name. ### `proof.yaml` -A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `state`. +A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `object`. ```yaml leaf: Bytes32 # string, hex encoded, with 0x prefix leaf_index: int # integer, decimal -branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix +branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix ``` ## Condition A test-runner can implement the following assertions: - Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify - against `has_tree_root(state)` and `proof`. + against `hash_tree_root(object)` and `branch`. - If the implementation supports generating merkle proofs, check that the - self-generated proof matches the `proof` provided with the test. + self-generated proof matches the `branch` provided with the test. diff --git a/tests/generators/README.md b/tests/generators/README.md index a84331dcb..0146ca35e 100644 --- a/tests/generators/README.md +++ b/tests/generators/README.md @@ -186,7 +186,7 @@ if __name__ == "__main__": ALTAIR: altair_mods, } - run_state_test_generators(runner_name="sanity", specs=specs, all_mods=all_mods) + run_state_test_generators(runner_name="sanity", all_mods=all_mods) ``` Here multiple phases load the configuration, and the stream of test cases is derived from a pytest file using the `eth2spec.gen_helpers.gen_from_tests.gen.run_state_test_generators` utility. Note that this helper generates all available tests of `TESTGEN_FORKS` forks of `ALL_CONFIGS` configs of the given runner. @@ -210,7 +210,7 @@ To add a new test generator that builds `New Tests`: with any dependencies it may need. Leave it empty if your generator has none. 3. Your generator is assumed to have a `main.py` file in its root. By adding the base generator to your requirements, you can make a generator really easily. See docs below. -4. Your generator is called with `-o some/file/path/for_testing/can/be_anything -c some/other/path/to_configs/`. +4. Your generator is called with `-o some/file/path/for_testing/can/be_anything --preset-list mainnet minimal`. The base generator helps you handle this; you only have to define test case providers. 5. Finally, add any linting or testing commands to the [circleci config file](../../.circleci/config.yml) if desired to increase code quality. diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index fc57fbe4e..84421e749 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -31,6 +31,10 @@ if __name__ == "__main__": # so no additional tests required. capella_mods = bellatrix_mods + # No epoch-processing changes in EIP4844 and previous testing repeats with new types, + # so no additional tests required. + eip4844_mods = capella_mods + # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [ # 'reveal_deadlines', @@ -43,6 +47,7 @@ if __name__ == "__main__": ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods) diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py index cb40e7cc9..de5af9b11 100644 --- a/tests/generators/finality/main.py +++ b/tests/generators/finality/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -7,12 +7,14 @@ if __name__ == "__main__": altair_mods = phase_0_mods # No additional Altair specific finality tests bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests capella_mods = bellatrix_mods # No additional Capella specific finality tests + eip4844_mods = capella_mods # No additional EIP4844 specific finality tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="finality", all_mods=all_mods) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 7982902c5..40e19a8ac 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -18,11 +18,14 @@ if __name__ == "__main__": ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) capella_mods = bellatrix_mods # No additional Capella specific fork choice tests + eip4844_mods = capella_mods # No additional Capella specific fork choice tests + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="fork_choice", all_mods=all_mods) diff --git a/tests/generators/forks/main.py b/tests/generators/forks/main.py index 628114979..42f3f3a1f 100644 --- a/tests/generators/forks/main.py +++ b/tests/generators/forks/main.py @@ -1,13 +1,14 @@ from typing import Iterable from eth2spec.test.helpers.constants import ( - PHASE0, ALTAIR, BELLATRIX, CAPELLA, + PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844, MINIMAL, MAINNET, ) from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from eth2spec.test.altair.fork import test_altair_fork_basic, test_altair_fork_random from eth2spec.test.bellatrix.fork import test_bellatrix_fork_basic, test_bellatrix_fork_random from eth2spec.test.capella.fork import test_capella_fork_basic, test_capella_fork_random +from eth2spec.test.eip4844.fork import test_eip4844_fork_basic, test_eip4844_fork_random from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests @@ -39,6 +40,8 @@ def _get_fork_tests_providers(): yield create_provider(test_bellatrix_fork_random, preset, ALTAIR, BELLATRIX) yield create_provider(test_capella_fork_basic, preset, BELLATRIX, CAPELLA) yield create_provider(test_capella_fork_random, preset, BELLATRIX, CAPELLA) + yield create_provider(test_eip4844_fork_basic, preset, CAPELLA, EIP4844) + yield create_provider(test_eip4844_fork_random, preset, CAPELLA, EIP4844) if __name__ == "__main__": diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index c8f5a83d5..a5c4eba9d 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -16,11 +16,13 @@ if __name__ == "__main__": ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) capella_mods = bellatrix_mods # No additional Capella specific genesis tests + eip4844_mods = capella_mods # No additional EIP4844 specific genesis tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="genesis", all_mods=all_mods) diff --git a/tests/generators/light_client/main.py b/tests/generators/light_client/main.py index 0ff3fe786..5d45bf39d 100644 --- a/tests/generators/light_client/main.py +++ b/tests/generators/light_client/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, EIP4844 from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators @@ -10,11 +10,13 @@ if __name__ == "__main__": ]} bellatrix_mods = altair_mods capella_mods = bellatrix_mods + eip4844_mods = capella_mods all_mods = { ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="light_client", all_mods=all_mods) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 1977ec217..bb879a647 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -31,6 +31,12 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) + _new_eip4844_mods = {key: 'eth2spec.test.eip4844.block_processing.test_process_' + key for key in [ + 'bls_to_execution_change', + 'withdrawals', + ]} + eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) + # TODO Custody Game testgen is disabled for now # _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [ # 'attestation', @@ -46,6 +52,7 @@ if __name__ == "__main__": ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="operations", all_mods=all_mods) diff --git a/tests/generators/random/Makefile b/tests/generators/random/Makefile index 8b77a3617..f57221ab4 100644 --- a/tests/generators/random/Makefile +++ b/tests/generators/random/Makefile @@ -6,7 +6,9 @@ all: rm -f ../../core/pyspec/eth2spec/test/altair/random/test_random.py rm -f ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py rm -f ../../core/pyspec/eth2spec/test/capella/random/test_random.py + rm -f ../../core/pyspec/eth2spec/test/eip4844/random/test_random.py python3 generate.py phase0 > ../../core/pyspec/eth2spec/test/phase0/random/test_random.py python3 generate.py altair > ../../core/pyspec/eth2spec/test/altair/random/test_random.py python3 generate.py bellatrix > ../../core/pyspec/eth2spec/test/bellatrix/random/test_random.py python3 generate.py capella > ../../core/pyspec/eth2spec/test/capella/random/test_random.py + python3 generate.py eip4844 > ../../core/pyspec/eth2spec/test/eip4844/random/test_random.py diff --git a/tests/generators/random/generate.py b/tests/generators/random/generate.py index 39d43b0aa..129d670fd 100644 --- a/tests/generators/random/generate.py +++ b/tests/generators/random/generate.py @@ -21,10 +21,12 @@ from eth2spec.test.utils.randomized_block_tests import ( randomize_state_altair, randomize_state_bellatrix, randomize_state_capella, + randomize_state_eip4844, random_block, random_block_altair_with_cycling_sync_committee_participation, random_block_bellatrix, random_block_capella, + random_block_eip4844, last_slot_in_epoch, random_slot_in_epoch, penultimate_slot_in_epoch, @@ -34,7 +36,7 @@ from eth2spec.test.utils.randomized_block_tests import ( transition_to_leaking, transition_without_leak, ) -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 # Ensure this many blocks are present in *each* randomized scenario @@ -272,5 +274,12 @@ if __name__ == "__main__": state_randomizer=randomize_state_capella, block_randomizer=random_block_capella, ) + if EIP4844 in sys.argv: + did_generate = True + run_generate_tests_to_std_out( + EIP4844, + state_randomizer=randomize_state_eip4844, + block_randomizer=random_block_eip4844, + ) if not did_generate: warnings.warn("no phase given for test generation") diff --git a/tests/generators/random/main.py b/tests/generators/random/main.py index 9983da96c..e36678771 100644 --- a/tests/generators/random/main.py +++ b/tests/generators/random/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators @@ -15,12 +15,16 @@ if __name__ == "__main__": capella_mods = {key: 'eth2spec.test.capella.random.test_' + key for key in [ 'random', ]} + eip4844_mods = {key: 'eth2spec.test.eip4844.random.test_' + key for key in [ + 'random', + ]} all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="random", all_mods=all_mods) diff --git a/tests/generators/rewards/main.py b/tests/generators/rewards/main.py index 33763144b..8958074bc 100644 --- a/tests/generators/rewards/main.py +++ b/tests/generators/rewards/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -16,12 +16,14 @@ if __name__ == "__main__": # Transaction fees are part of the execution-layer. bellatrix_mods = altair_mods capella_mods = bellatrix_mods + eip4844_mods = capella_mods all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="rewards", all_mods=all_mods) diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index bb3c954c2..9dd6d7ac0 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, EIP4844 from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods @@ -23,11 +23,17 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) + _new_eip4844_mods = {key: 'eth2spec.test.eip4844.sanity.test_' + key for key in [ + 'blocks', + ]} + eip4844_mods = combine_mods(_new_eip4844_mods, capella_mods) + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="sanity", all_mods=all_mods) diff --git a/tests/generators/sync/main.py b/tests/generators/sync/main.py index eba3ac152..8fb395053 100644 --- a/tests/generators/sync/main.py +++ b/tests/generators/sync/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA +from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, EIP4844 if __name__ == "__main__": @@ -7,10 +7,12 @@ if __name__ == "__main__": 'optimistic', ]} capella_mods = bellatrix_mods + eip4844_mods = capella_mods all_mods = { BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, + EIP4844: eip4844_mods, } run_state_test_generators(runner_name="sync", all_mods=all_mods)