diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 000000000..3e23d1910 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,133 @@ +name: Run spec tests and linter + +defaults: + run: + shell: zsh {0} + +env: + TEST_PRESET_TYPE: "minimal" + DEFAULT_BRANCH: "dev" + +# Run tests on workflow_Dispatch +on: + push: + branches: + - dev + - master + pull_request: + workflow_dispatch: + inputs: + test_preset_type: + default: minimal + description: Type of test to run, either mainnet or minimal + type: string + required: true + commitRef: + description: The branch, tag or SHA to checkout and build from + default: dev + required: true + schedule: + - cron: '0 0 * * *' + +jobs: + precleanup: + runs-on: self-hosted + if: always() + steps: + - name: 'Cleanup build folder' + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ + setup-env: + runs-on: self-hosted + needs: precleanup + steps: + - name: Checkout this repo + uses: actions/checkout@v3.2.0 + with: + ref: ${{ github.event.inputs.commitRef || env.DEFAULT_BRANCH }} + - uses: actions/cache@v3.2.2 + id: cache-git + with: + path: ./* + key: ${{ github.sha }} + + table_of_contents: + runs-on: self-hosted + needs: setup-env + steps: + - uses: actions/cache@v3.2.2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + - name: Check table of contents + run: sudo npm install -g doctoc@2 && make check_toc + + codespell: + runs-on: self-hosted + needs: setup-env + steps: + - name: Check codespell + run: pip install 'codespell<3.0.0,>=2.0.0' --user && make codespell + + lint: + runs-on: self-hosted + needs: setup-env + steps: + - name: Run linter for pyspec + run: make lint + - name: Run linter for test generators + run: make lint_generators + + pyspec-tests: + runs-on: self-hosted + needs: [setup-env,lint,codespell,table_of_contents] + strategy: + matrix: + version: ["phase0", "altair", "bellatrix", "capella", "eip4844"] + steps: + - uses: actions/cache@v3.2.2 + id: restore-build + with: + path: ./* + key: ${{ github.sha }} + - name: set TEST_PRESET_TYPE + if: github.event.inputs.test_preset_type != '' + run: | + echo "spec_test_preset_type=${{ github.event.inputs.test_preset_type || env.TEST_PRESET_TYPE }}" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: ${{ (github.event_name == 'push' && github.ref_name != 'master') || github.event_name == 'pull_request' }} + run: | + echo "spec_test_preset_type=${{ env.TEST_PRESET_TYPE}}" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + run: | + echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV + - name: set TEST_PRESET_TYPE + if: github.event.schedule=='0 0 * * *' + run: | + echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV + - name: Install pyspec requirements + run: make install_test + - name: test-${{ matrix.version }} + run: make citest fork=${{ matrix.version }} TEST_PRESET_TYPE=${{env.spec_test_preset_type}} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: test-${{ matrix.version }} + path: tests/core/pyspec/test-reports + + cleanup: + runs-on: self-hosted + needs: [setup-env,pyspec-tests,codespell,lint,table_of_contents] + if: always() + steps: + - name: 'Cleanup build folder' + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ \ No newline at end of file diff --git a/Makefile b/Makefile index 645ed7006..ac769b200 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SOLIDITY_DEPOSIT_CONTRACT_SOURCE = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/deposit_cont SOLIDITY_FILE_NAME = deposit_contract.json DEPOSIT_CONTRACT_TESTER_DIR = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/web3_tester CONFIGS_DIR = ./configs - +TEST_PRESET_TYPE ?= minimal # Collect a list of generator names GENERATORS = $(sort $(dir $(wildcard $(GENERATOR_DIR)/*/.))) # Map this list of generator paths to "gen_{generator name}" entries @@ -96,30 +96,30 @@ generate_tests: $(GENERATOR_TARGETS) # "make pyspec" to create the pyspec for all phases. pyspec: - . venv/bin/activate; python3 setup.py pyspecdev + python3 -m venv venv; . venv/bin/activate; python3 setup.py pyspecdev # installs the packages to run pyspec tests install_test: python3 -m venv venv; . venv/bin/activate; python3 -m pip install -e .[lint]; python3 -m pip install -e .[test] -# Testing against `minimal` config by default +# Testing against `minimal` or `mainnet` config by default test: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec + python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.$(TEST_PRESET_TYPE) --cov=eth2spec.altair.$(TEST_PRESET_TYPE) --cov=eth2spec.bellatrix.$(TEST_PRESET_TYPE) --cov=eth2spec.capella.$(TEST_PRESET_TYPE) --cov=eth2spec.eip4844.$(TEST_PRESET_TYPE) --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec -# Testing against `minimal` config by default +# Testing against `minimal` or `mainnet` config by default find_test: pyspec . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec + python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.$(TEST_PRESET_TYPE) --cov=eth2spec.altair.$(TEST_PRESET_TYPE) --cov=eth2spec.bellatrix.$(TEST_PRESET_TYPE) --cov=eth2spec.capella.$(TEST_PRESET_TYPE) --cov=eth2spec.eip4844.$(TEST_PRESET_TYPE) --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec citest: pyspec mkdir -p $(TEST_REPORT_DIR); ifdef fork . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --bls-type=milagro --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec + python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec else . venv/bin/activate; cd $(PY_SPEC_DIR); \ - python3 -m pytest -n 4 --bls-type=milagro --junitxml=test-reports/test_results.xml eth2spec + python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec endif @@ -135,7 +135,7 @@ check_toc: $(MARKDOWN_FILES:=.toc) rm $*.tmp codespell: - codespell . --skip ./.git -I .codespell-whitelist + codespell . --skip "./.git,./venv,$(PY_SPEC_DIR)/.mypy_cache" -I .codespell-whitelist # TODO: add future protocol upgrade patch packages to linting. # NOTE: we use `pylint` just for catching unused arguments in spec code diff --git a/README.md b/README.md index 07b78c0d5..bb69a452a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Features are researched and developed in parallel, and then consolidated into se | Code Name or Topic | Specs | Notes | | - | - | - | | Capella (tentative) | | -| EIP4844 (tentative) | | +| EIP4844 (tentative) | | | Sharding (outdated) | | | Custody Game (outdated) | | Dependent on sharding | | Data Availability Sampling (outdated) | | | diff --git a/setup.py b/setup.py index be8be6d90..6fc26f3c0 100644 --- a/setup.py +++ b/setup.py @@ -638,34 +638,6 @@ T = TypeVar('T') # For generic function @classmethod def sundry_functions(cls) -> str: return super().sundry_functions() + '\n\n' + ''' -# -# Temporarily disable Withdrawals functions for EIP4844 testnets -# - - -def no_op(fn): # type: ignore - # pylint: disable=unused-argument - def wrapper(*args, **kw): # type: ignore - return None - return wrapper - - -def get_empty_list_result(fn): # type: ignore - # pylint: disable=unused-argument - 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) -> PyUnion[BlobsSidecar, str]: # pylint: disable=unused-argument return "TEST"''' @@ -1020,6 +992,7 @@ class PySpecCommand(Command): self.md_doc_paths += """ specs/eip4844/beacon-chain.md specs/eip4844/fork.md + specs/eip4844/fork-choice.md specs/eip4844/polynomial-commitments.md specs/eip4844/p2p-interface.md specs/eip4844/validator.md diff --git a/specs/altair/light-client/full-node.md b/specs/altair/light-client/full-node.md index 53ba4dc82..7f0b7bc39 100644 --- a/specs/altair/light-client/full-node.md +++ b/specs/altair/light-client/full-node.md @@ -11,6 +11,7 @@ - [Introduction](#introduction) - [Helper functions](#helper-functions) - [`compute_merkle_proof_for_state`](#compute_merkle_proof_for_state) + - [`block_to_light_client_header`](#block_to_light_client_header) - [Deriving light client data](#deriving-light-client-data) - [`create_light_client_bootstrap`](#create_light_client_bootstrap) - [`create_light_client_update`](#create_light_client_update) @@ -34,6 +35,19 @@ def compute_merkle_proof_for_state(state: BeaconState, ... ``` +### `block_to_light_client_header` + +```python +def block_to_light_client_header(block: SignedBeaconBlock) -> BeaconBlockHeader: + return BeaconBlockHeader( + slot=block.message.slot, + proposer_index=block.message.proposer_index, + parent_root=block.message.parent_root, + state_root=block.message.state_root, + body_root=hash_tree_root(block.message.body), + ) +``` + ## Deriving light client data Full nodes are expected to derive light client data from historic blocks and states and provide it to other clients. @@ -55,13 +69,7 @@ def create_light_client_bootstrap(state: BeaconState, assert hash_tree_root(header) == hash_tree_root(block.message) return LightClientBootstrap( - header=BeaconBlockHeader( - slot=state.latest_block_header.slot, - proposer_index=state.latest_block_header.proposer_index, - parent_root=state.latest_block_header.parent_root, - state_root=hash_tree_root(state), - body_root=state.latest_block_header.body_root, - ), + header=block_to_light_client_header(block), current_sync_committee=state.current_sync_committee, current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX), ) @@ -103,42 +111,30 @@ def create_light_client_update(state: BeaconState, assert hash_tree_root(attested_header) == hash_tree_root(attested_block.message) == block.message.parent_root update_attested_period = compute_sync_committee_period_at_slot(attested_block.message.slot) + update = LightClientUpdate() + + update.attested_header = block_to_light_client_header(attested_block) + # `next_sync_committee` is only useful if the message is signed by the current sync committee if update_attested_period == update_signature_period: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = SyncCommittee() - next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = compute_merkle_proof_for_state( + attested_state, NEXT_SYNC_COMMITTEE_INDEX) # Indicate finality whenever possible if finalized_block is not None: if finalized_block.message.slot != GENESIS_SLOT: - finalized_header = BeaconBlockHeader( - slot=finalized_block.message.slot, - proposer_index=finalized_block.message.proposer_index, - parent_root=finalized_block.message.parent_root, - state_root=finalized_block.message.state_root, - body_root=hash_tree_root(finalized_block.message.body), - ) - assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root + update.finalized_header = block_to_light_client_header(finalized_block) + assert hash_tree_root(update.finalized_header) == attested_state.finalized_checkpoint.root else: assert attested_state.finalized_checkpoint.root == Bytes32() - finalized_header = BeaconBlockHeader() - finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) - else: - finalized_header = BeaconBlockHeader() - finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] + update.finality_branch = compute_merkle_proof_for_state( + attested_state, FINALIZED_ROOT_INDEX) - return LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=block.message.body.sync_aggregate, - signature_slot=block.message.slot, - ) + update.sync_aggregate = block.message.body.sync_aggregate + update.signature_slot = block.message.slot + + return update ``` Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods. diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 20f4a5dd4..9a04f2f38 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -18,6 +18,7 @@ - [`Withdrawal`](#withdrawal) - [`BLSToExecutionChange`](#blstoexecutionchange) - [`SignedBLSToExecutionChange`](#signedblstoexecutionchange) + - [`HistoricalSummary`](#historicalsummary) - [Extended Containers](#extended-containers) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) @@ -29,6 +30,8 @@ - [`is_fully_withdrawable_validator`](#is_fully_withdrawable_validator) - [`is_partially_withdrawable_validator`](#is_partially_withdrawable_validator) - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Epoch processing](#epoch-processing) + - [Historical summaries updates](#historical-summaries-updates) - [Block processing](#block-processing) - [New `get_expected_withdrawals`](#new-get_expected_withdrawals) - [New `process_withdrawals`](#new-process_withdrawals) @@ -50,6 +53,11 @@ to validator withdrawals. Including: * Operation to change from `BLS_WITHDRAWAL_PREFIX` to `ETH1_ADDRESS_WITHDRAWAL_PREFIX` versioned withdrawal credentials to enable withdrawals for a validator. +Another new feature is the new independent state and block historical accumulators +that replace the original singular historical roots. With these accumulators, it becomes possible to validate +the entire block history that led up to that particular state without any additional information +beyond the state and the blocks. + ## Custom types We define the following Python custom types for type hinting and readability: @@ -115,6 +123,18 @@ class SignedBLSToExecutionChange(Container): signature: BLSSignature ``` +#### `HistoricalSummary` + +```python +class HistoricalSummary(Container): + """ + `HistoricalSummary` matches the components of the phase0 `HistoricalBatch` + making the two hash_tree_root-compatible. + """ + block_summary_root: Root + state_summary_root: Root +``` + ### Extended Containers #### `ExecutionPayload` @@ -196,7 +216,7 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_summaries # Eth1 eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -226,6 +246,8 @@ class BeaconState(Container): # Withdrawals next_withdrawal_index: WithdrawalIndex # [New in Capella] next_withdrawal_validator_index: ValidatorIndex # [New in Capella] + # Deep history valid from Capella onwards + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] # [New in Capella] ``` ## Helpers @@ -270,6 +292,40 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> ## Beacon chain state transition function +### Epoch processing + +*Note*: The function `process_historical_summaries_update` replaces `process_historical_roots_update` in Bellatrix. + +```python +def process_epoch(state: BeaconState) -> None: + process_justification_and_finalization(state) + process_inactivity_updates(state) + process_rewards_and_penalties(state) + process_registry_updates(state) + process_slashings(state) + process_eth1_data_reset(state) + process_effective_balance_updates(state) + process_slashings_reset(state) + process_randao_mixes_reset(state) + process_historical_summaries_update(state) # [Modified in Capella] + process_participation_flag_updates(state) + process_sync_committee_updates(state) +``` + +#### Historical summaries updates + +```python +def process_historical_summaries_update(state: BeaconState) -> None: + # Set historical block root accumulator. + next_epoch = Epoch(get_current_epoch(state) + 1) + if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: + historical_summary = HistoricalSummary( + block_summary_root=hash_tree_root(state.block_roots), + state_summary_root=hash_tree_root(state.state_roots), + ) + state.historical_summaries.append(historical_summary) +``` + ### Block processing ```python diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 39b8ed00f..f681ab951 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -22,8 +22,6 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) - - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - - [`is_data_available`](#is_data_available) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) @@ -33,7 +31,6 @@ - [`process_execution_payload`](#process_execution_payload) - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) - - [Disabling Withdrawals](#disabling-withdrawals) @@ -146,45 +143,6 @@ class ExecutionPayloadHeader(Container): ### Misc -#### `validate_blobs_sidecar` - -```python -def validate_blobs_sidecar(slot: Slot, - beacon_block_root: Root, - expected_kzg_commitments: Sequence[KZGCommitment], - blobs_sidecar: BlobsSidecar) -> None: - assert slot == blobs_sidecar.beacon_block_slot - assert beacon_block_root == blobs_sidecar.beacon_block_root - blobs = blobs_sidecar.blobs - kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof - assert len(expected_kzg_commitments) == len(blobs) - - assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) -``` - -#### `is_data_available` - -The implementation of `is_data_available` is meant to change with later sharding upgrades. -Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, -and validate the sidecar with `validate_blobs_sidecar`. - -The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. - -```python -def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: - # `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available. - sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) - - # For testing, `retrieve_blobs_sidecar` returns "TEST". - # TODO: Remove it once we have a way to inject `BlobsSidecar` into tests. - if isinstance(sidecar, str): - return True - - validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) - return True -``` - - #### `kzg_commitment_to_versioned_hash` ```python @@ -240,9 +198,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) process_blob_kzg_commitments(state, block.body) # [New in EIP-4844] - - # New in EIP-4844 - assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) ``` #### Execution payload @@ -345,11 +300,3 @@ 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-choice.md b/specs/eip4844/fork-choice.md new file mode 100644 index 000000000..8dea28ded --- /dev/null +++ b/specs/eip4844/fork-choice.md @@ -0,0 +1,137 @@ +# EIP-4844 -- Fork Choice + +## Table of contents + + + + +- [Introduction](#introduction) +- [Containers](#containers) + - [`BlobsSidecar`](#blobssidecar) +- [Helpers](#helpers) + - [`validate_blobs_sidecar`](#validate_blobs_sidecar) + - [`is_data_available`](#is_data_available) +- [Updated fork-choice handlers](#updated-fork-choice-handlers) + - [`on_block`](#on_block) + + + + +## Introduction + +This is the modification of the fork choice accompanying the EIP-4844 upgrade. + +## Containers + +### `BlobsSidecar` + +```python +class BlobsSidecar(Container): + beacon_block_root: Root + beacon_block_slot: Slot + blobs: List[Blob, MAX_BLOBS_PER_BLOCK] + kzg_aggregated_proof: KZGProof +``` + +## Helpers + +#### `validate_blobs_sidecar` + +```python +def validate_blobs_sidecar(slot: Slot, + beacon_block_root: Root, + expected_kzg_commitments: Sequence[KZGCommitment], + blobs_sidecar: BlobsSidecar) -> None: + assert slot == blobs_sidecar.beacon_block_slot + assert beacon_block_root == blobs_sidecar.beacon_block_root + blobs = blobs_sidecar.blobs + kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof + assert len(expected_kzg_commitments) == len(blobs) + + assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) +``` + +#### `is_data_available` + +The implementation of `is_data_available` will become more sophisticated during later scaling upgrades. +Initially, verification requires every verifying actor to retrieve the matching `BlobsSidecar`, +and validate the sidecar with `validate_blobs_sidecar`. + +The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned. + +```python +def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: + # `retrieve_blobs_sidecar` is implementation and context dependent, raises an exception if not available. + # Note: the p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` + sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) + + # For testing, `retrieve_blobs_sidecar` returns "TEST". + # TODO: Remove it once we have a way to inject `BlobsSidecar` into tests. + if isinstance(sidecar, str): + return True + + validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) + return True +``` + +## Updated fork-choice handlers + +### `on_block` + +*Note*: The only modification is the addition of the verification of transition block conditions. + +```python +def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: + """ + Run ``on_block`` upon receiving a new block. + """ + block = signed_block.message + # Parent block must be known + assert block.parent_root in store.block_states + # Make a copy of the state to avoid mutability issues + pre_state = copy(store.block_states[block.parent_root]) + # Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past. + assert get_current_slot(store) >= block.slot + + # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) + finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + assert block.slot > finalized_slot + # Check block is a descendant of the finalized block at the checkpoint finalized slot + assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root + + # [New in EIP-4844] + # Check if blob data is available + # If not, this block MAY be queued and subsequently considered when blob data becomes available + assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) + + # Check the block is valid and compute the post-state + state = pre_state.copy() + state_transition(state, signed_block, True) + + # Check the merge transition + if is_merge_transition_block(pre_state, block.body): + validate_merge_block(block) + + # Add new block to the store + store.blocks[hash_tree_root(block)] = block + # Add new state for this block to the store + store.block_states[hash_tree_root(block)] = state + + # Add proposer score boost if the block is timely + time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT + is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + if get_current_slot(store) == block.slot and is_before_attesting_interval: + store.proposer_boost_root = hash_tree_root(block) + + # Update justified checkpoint + if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: + if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: + store.best_justified_checkpoint = state.current_justified_checkpoint + if should_update_justified_checkpoint(store, state.current_justified_checkpoint): + store.justified_checkpoint = state.current_justified_checkpoint + + # Update finalized checkpoint + if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: + store.finalized_checkpoint = state.finalized_checkpoint + store.justified_checkpoint = state.current_justified_checkpoint +``` diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index c091d0398..f2e8d9ac6 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -12,7 +12,6 @@ The specification of these changes continues in the same format as the network s - [Configuration](#configuration) - [Containers](#containers) - - [`BlobsSidecar`](#blobssidecar) - [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) @@ -41,16 +40,6 @@ The specification of these changes continues in the same format as the network s ## Containers -### `BlobsSidecar` - -```python -class BlobsSidecar(Container): - beacon_block_root: Root - beacon_block_slot: Slot - blobs: List[Blob, MAX_BLOBS_PER_BLOCK] - kzg_aggregated_proof: KZGProof -``` - ### `SignedBeaconBlockAndBlobsSidecar` ```python @@ -141,8 +130,8 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: **Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/` -After `EIP4844_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1` -clients MUST support requesting blocks by root for pre-fork-epoch blocks. +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)`: @@ -186,7 +175,7 @@ No more than `MAX_REQUEST_BLOCKS` may be requested at a time. 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 support requesting blocks and sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, EIP4844_FORK_EPOCH)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers SHOULD respond with error code `3: ResourceUnavailable`. 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. @@ -219,7 +208,7 @@ may not be available beyond the initial distribution via gossip. Before consuming the next response chunk, the response reader SHOULD verify the blobs sidecar is well-formatted and correct w.r.t. the expected KZG commitments through `validate_blobs_sidecar`. -`BlobsSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip. +`BlobsSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip and to sync within the `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` window. The request MUST be encoded as an SSZ-container. @@ -227,7 +216,7 @@ The response MUST consist of zero or more `response_chunk`. Each _successful_ `response_chunk` MUST contain a single `BlobsSidecar` payload. Clients MUST keep a record of signed blobs sidecars seen on the epoch range -`[max(GENESIS_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS), current_epoch]` +`[max(current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, EIP4844_FORK_EPOCH), current_epoch]` where `current_epoch` is defined by the current wall-clock time, and clients MUST support serving requests of blocks on this range. diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 6aef9c611..8884457ed 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -370,8 +370,9 @@ def verify_kzg_proof_impl(polynomial_kzg: KZGCommitment, ```python def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof: """ - Compute KZG proof at point `z` with `polynomial` being in evaluation form - Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) + Compute KZG proof at point `z` with `polynomial` being in evaluation form. + Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z). + Public method. """ y = evaluate_polynomial_in_evaluation_form(polynomial, z) polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial] diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 79c724242..b73f10e65 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.3.0-alpha.2 +1.3.0-rc.0 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 465fa629f..5d802bbb3 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 @@ -10,8 +10,8 @@ from eth2spec.test.context import ( @spec_state_test def test_current_sync_committee_merkle_proof(spec, state): yield "object", state - current_sync_committee_branch = \ - spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX) + 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, @@ -31,8 +31,8 @@ def test_current_sync_committee_merkle_proof(spec, state): @spec_state_test def test_next_sync_committee_merkle_proof(spec, state): yield "object", state - next_sync_committee_branch = \ - spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) + 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, @@ -52,8 +52,8 @@ def test_next_sync_committee_merkle_proof(spec, state): @spec_state_test def test_finality_root_merkle_proof(spec, state): yield "object", state - finality_branch = \ - spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + 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/altair/light_client/test_update_ranking.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py index 23ad79584..bde70a940 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py @@ -9,45 +9,23 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) -from math import floor -def create_update(spec, test, with_next, with_finality, participation_rate): +def create_test_update(spec, test, with_next, with_finality, participation_rate): attested_state, attested_block, finalized_block = test - num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) - - attested_header = signed_block_to_header(spec, attested_block) - - if with_next: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - if with_finality: - finalized_header = signed_block_to_header(spec, finalized_block) - finality_branch = spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) - else: - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - sync_aggregate, signature_slot = get_sync_aggregate(spec, attested_state, num_participants) - - return spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + return create_update( + spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate, ) @@ -84,76 +62,76 @@ def test_update_ranking(spec, state): # Create updates (in descending order of quality) updates = [ # Updates with sync committee finality - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), # Updates without sync committee finality - create_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), # Updates without indication of any finality - create_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), # Updates with sync committee finality but no `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), # Updates without sync committee finality and also no `next_sync_committee` - create_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), # Updates without indication of any finality nor `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), # Updates with low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), # Updates with very low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), ] yield "updates", updates diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py index bf09cc30e..a72f1980b 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py @@ -11,43 +11,44 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - initialize_light_client_store, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) +def setup_test(spec, state): + trusted_block = spec.SignedBeaconBlock() + trusted_block.message.state_root = state.hash_tree_root() + trusted_block_root = trusted_block.message.hash_tree_root() + bootstrap = spec.create_light_client_bootstrap(state, trusted_block) + store = spec.initialize_light_client_store(trusted_block_root, bootstrap) + store.next_sync_committee = state.next_sync_committee + + return (trusted_block, store) + + @with_altair_and_later @spec_state_test_with_matching_config def test_process_light_client_update_not_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Block at slot 1 doesn't increase sync committee period, so it won't force update store.finalized_header attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) - - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] + signature_slot = state.slot + 1 # Ensure that finality checkpoint is genesis assert state.finalized_checkpoint.epoch == 0 - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -64,7 +65,7 @@ def test_process_light_client_update_not_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_at_period_boundary(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to slot before next sync committee period so that next block is final one in period next_slots(spec, state, spec.UPDATE_TIMEOUT - 2) @@ -73,25 +74,16 @@ def test_process_light_client_update_at_period_boundary(spec, state): assert store_period == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -108,7 +100,7 @@ def test_process_light_client_update_at_period_boundary(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to next sync committee period next_slots(spec, state, spec.UPDATE_TIMEOUT) @@ -117,26 +109,16 @@ def test_process_light_client_update_timeout(spec, state): assert store_period + 1 == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # Sync committee is updated - next_sync_committee = state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=True, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -153,7 +135,7 @@ def test_process_light_client_update_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_finality_updated(spec, state): - store = initialize_light_client_store(spec, state) + _, store = setup_test(spec, state) # Change finality blocks = [] @@ -169,28 +151,21 @@ def test_process_light_client_update_finality_updated(spec, state): assert store_period == update_period attested_block = blocks[-1] - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # Updated sync_committee and finality - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] + # Updated finality finalized_block = blocks[spec.SLOTS_PER_EPOCH - 1] - finalized_header = signed_block_to_header(spec, finalized_block) - assert finalized_header.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - assert finalized_header.hash_tree_root() == state.finalized_checkpoint.root - finality_branch = spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + assert finalized_block.message.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) + assert finalized_block.message.hash_tree_root() == state.finalized_checkpoint.root - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=finalized_block, + with_next=False, + with_finality=True, + participation_rate=1.0, ) spec.process_light_client_update(store, update, signature_slot, state.genesis_validators_root) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 974719f92..eb56e368a 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -64,6 +64,7 @@ def test_from_syncing_to_invalid(spec, state): block.body.execution_payload.parent_hash = ( block_hashes[f'chain_a_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_a_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_a_{i}'] = block.body.execution_payload.block_hash @@ -80,6 +81,7 @@ def test_from_syncing_to_invalid(spec, state): block.body.execution_payload.parent_hash = ( block_hashes[f'chain_b_{i - 1}'] if i != 0 else block_hashes['block_0'] ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_b_{i}'] = block.body.execution_payload.block_hash @@ -92,9 +94,13 @@ def test_from_syncing_to_invalid(spec, state): # Now add block 4 to chain `b` with INVALID block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = signed_blocks_b[-1].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes['chain_b_3'] = block.body.execution_payload.block_hash + # Ensure that no duplicate block hashes + assert len(block_hashes) == len(set(block_hashes.values())) + signed_block = state_transition_and_sign_block(spec, state, block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, 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 6a1ba5b36..094de5eeb 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,8 +1,7 @@ -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_phases, always_bls +from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True): @@ -38,14 +37,14 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v yield 'post', state -@with_phases([CAPELLA]) +@with_capella_and_later @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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_not_activated(spec, state): validator_index = 3 @@ -63,7 +62,7 @@ def test_success_not_activated(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_in_activation_queue(spec, state): validator_index = 3 @@ -81,7 +80,7 @@ def test_success_in_activation_queue(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_in_exit_queue(spec, state): validator_index = 3 @@ -94,7 +93,7 @@ def test_success_in_exit_queue(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_exited(spec, state): validator_index = 4 @@ -111,7 +110,7 @@ def test_success_exited(spec, state): assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_withdrawable(spec, state): validator_index = 4 @@ -129,7 +128,7 @@ def test_success_withdrawable(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_val_index_out_of_range(spec, state): # Create for one validator beyond the validator list length @@ -138,7 +137,7 @@ def test_invalid_val_index_out_of_range(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_already_0x01(spec, state): # Create for one validator beyond the validator list length @@ -150,7 +149,7 @@ def test_invalid_already_0x01(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_from_bls_pubkey(spec, state): # Create for one validator beyond the validator list length @@ -164,7 +163,7 @@ def test_invalid_incorrect_from_bls_pubkey(spec, state): yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test @always_bls def test_invalid_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 685d17651..e0603d301 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,8 +1,7 @@ from eth2spec.test.context import ( spec_state_test, - with_phases, + with_capella_and_later, ) -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, @@ -11,7 +10,7 @@ from eth2spec.test.helpers.deposits import ( from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable -@with_phases([CAPELLA]) +@with_capella_and_later @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 fa3806d2c..674231096 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 @@ -4,9 +4,9 @@ from eth2spec.test.context import ( spec_state_test, expect_assertion_error, with_presets, - with_phases, + with_capella_and_later, ) -from eth2spec.test.helpers.constants import MAINNET, MINIMAL, CAPELLA +from eth2spec.test.helpers.constants import MAINNET, MINIMAL from eth2spec.test.helpers.execution_payload import ( build_empty_execution_payload, compute_el_block_hash, @@ -97,7 +97,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with return expected_withdrawals -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_zero_expected_withdrawals(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 @@ -108,7 +108,7 @@ def test_success_zero_expected_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_full_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -125,7 +125,7 @@ def test_success_one_full_withdrawal(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawal(spec, state): fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals( @@ -145,7 +145,7 @@ def test_success_one_partial_withdrawal(spec, state): ) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_max_per_slot(spec, state): num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2 @@ -163,7 +163,7 @@ def test_success_max_per_slot(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_fully_withdrawable_in_one_sweep(spec, state): @@ -182,7 +182,7 @@ def test_success_all_fully_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_fully_withdrawable(spec, state): @@ -201,7 +201,7 @@ def test_success_all_fully_withdrawable(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MAINNET], reason="too few validators with minimal config") @spec_state_test def test_success_all_partially_withdrawable_in_one_sweep(spec, state): @@ -220,7 +220,7 @@ def test_success_all_partially_withdrawable_in_one_sweep(spec, state): partial_withdrawals_indices=partial_withdrawals_indices) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="too many validators with mainnet config") @spec_state_test def test_success_all_partially_withdrawable(spec, state): @@ -243,7 +243,7 @@ def test_success_all_partially_withdrawable(spec, state): # Failure cases in which the number of withdrawals in the execution_payload is incorrect # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): next_slot(spec, state) @@ -260,7 +260,7 @@ def test_invalid_non_withdrawable_non_empty_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -273,7 +273,7 @@ def test_invalid_one_expected_full_withdrawal_and_none_in_withdrawals(spec, stat yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -286,7 +286,7 @@ def test_invalid_one_expected_partial_withdrawal_and_none_in_withdrawals(spec, s yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=2) @@ -299,7 +299,7 @@ def test_invalid_one_expected_full_withdrawal_and_duplicate_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=2) @@ -312,7 +312,7 @@ def test_invalid_two_expected_partial_withdrawal_and_duplicate_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_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) @@ -325,7 +325,7 @@ def test_invalid_max_per_slot_full_withdrawals_and_one_less_in_withdrawals(spec, yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_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) @@ -338,7 +338,7 @@ def test_invalid_max_per_slot_partial_withdrawals_and_one_less_in_withdrawals(sp yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -351,7 +351,7 @@ def test_invalid_a_lot_fully_withdrawable_too_few_in_withdrawals(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -364,7 +364,7 @@ def test_invalid_a_lot_partially_withdrawable_too_few_in_withdrawals(spec, state yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_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, @@ -382,7 +382,7 @@ def test_invalid_a_lot_mixed_withdrawable_in_queue_too_few_in_withdrawals(spec, # Failure cases in which the withdrawals in the execution_payload are incorrect # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_withdrawal_index(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -395,7 +395,7 @@ def test_invalid_incorrect_withdrawal_index(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_address_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -408,7 +408,7 @@ def test_invalid_incorrect_address_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_address_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=1) @@ -421,7 +421,7 @@ def test_invalid_incorrect_address_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_amount_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -434,7 +434,7 @@ def test_invalid_incorrect_amount_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_incorrect_amount_partial(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=1) @@ -447,7 +447,7 @@ def test_invalid_incorrect_amount_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_of_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -466,7 +466,7 @@ def test_invalid_one_of_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_one_of_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -485,7 +485,7 @@ def test_invalid_one_of_many_incorrectly_partial(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_many_incorrectly_full(spec, state): prepare_expected_withdrawals(spec, state, num_full_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -504,7 +504,7 @@ def test_invalid_many_incorrectly_full(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, valid=False) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_many_incorrectly_partial(spec, state): prepare_expected_withdrawals(spec, state, num_partial_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4) @@ -527,7 +527,7 @@ def test_invalid_many_incorrectly_partial(spec, state): # More full withdrawal cases # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -541,7 +541,7 @@ def test_withdrawable_epoch_but_0_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -555,7 +555,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state): current_epoch = spec.get_current_epoch(state) @@ -569,7 +569,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_no_withdrawals_but_some_next_epoch(spec, state): current_epoch = spec.get_current_epoch(state) @@ -583,7 +583,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_all_withdrawal(spec, state): # Make all validators withdrawable @@ -619,25 +619,25 @@ def run_random_full_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_0(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(444)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_1(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(420)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_2(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(200)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_full_withdrawals_3(spec, state): yield from run_random_full_withdrawals_test(spec, state, random.Random(2000000)) @@ -647,7 +647,7 @@ def test_random_full_withdrawals_3(spec, state): # More partial withdrawal cases # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -663,7 +663,7 @@ def test_success_no_max_effective_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_no_excess_balance(spec, state): validator_index = len(state.validators) // 2 @@ -679,7 +679,7 @@ def test_success_no_excess_balance(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=0) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_excess_balance_but_no_max_effective_balance(spec, state): validator_index = len(state.validators) // 2 @@ -696,7 +696,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_not_yet_active(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -710,7 +710,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_in_exit_queue(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -725,7 +725,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_exited(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -739,7 +739,7 @@ def test_success_one_partial_withdrawable_exited(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=1) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_active_and_slashed(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -753,7 +753,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_one_partial_withdrawable_exited_and_slashed(spec, state): validator_index = min(len(state.validators) // 2, spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP - 1) @@ -768,7 +768,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_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_two_partial_withdrawable(spec, state): set_validator_partially_withdrawable(spec, state, 0) @@ -779,7 +779,7 @@ def test_success_two_partial_withdrawable(spec, state): yield from run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=2) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_max_partial_withdrawable(spec, state): # Sanity check that this test works for this state @@ -794,7 +794,7 @@ def test_success_max_partial_withdrawable(spec, state): spec, state, execution_payload, num_expected_withdrawals=spec.MAX_WITHDRAWALS_PER_PAYLOAD) -@with_phases([CAPELLA]) +@with_capella_and_later @with_presets([MINIMAL], reason="not enough validators with mainnet config") @spec_state_test def test_success_max_plus_one_withdrawable(spec, state): @@ -833,37 +833,37 @@ def run_random_partial_withdrawals_test(spec, state, rng): yield from run_withdrawals_processing(spec, state, execution_payload) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_0(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(0)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_1(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(1)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_2(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(2)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_3(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(3)) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_random_partial_withdrawals_4(spec, state): yield from run_random_partial_withdrawals_test(spec, state, random.Random(4)) -@with_phases([CAPELLA]) +@with_capella_and_later @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/eip4844/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip4844/block_processing/__init__.py rename to tests/core/pyspec/eth2spec/test/capella/epoch_processing/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_summaries_update.py b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_summaries_update.py new file mode 100644 index 000000000..c5465d328 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/capella/epoch_processing/test_process_historical_summaries_update.py @@ -0,0 +1,26 @@ +from eth2spec.test.context import ( + spec_state_test, + with_capella_and_later, +) +from eth2spec.test.helpers.epoch_processing import ( + run_epoch_processing_with +) + + +def run_process_historical_summaries_update(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_historical_summaries_update') + + +@with_capella_and_later +@spec_state_test +def test_historical_summaries_accumulator(spec, state): + # skip ahead to near the end of the historical batch period (excl block before epoch processing) + state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 + pre_historical_summaries = state.historical_summaries.copy() + + yield from run_process_historical_summaries_update(spec, state) + + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + summary = state.historical_summaries[len(state.historical_summaries) - 1] + assert summary.block_summary_root == state.block_roots.hash_tree_root() + assert summary.state_summary_root == state.state_roots.hash_tree_root() 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 1df046c9d..1cd1c1317 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,6 @@ from eth2spec.test.context import ( - with_phases, spec_state_test + with_capella_and_later, spec_state_test ) -from eth2spec.test.helpers.constants import CAPELLA from eth2spec.test.helpers.state import ( state_transition_and_sign_block, ) @@ -25,7 +24,7 @@ from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits # BLSToExecutionChange # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_bls_change(spec, state): index = 0 @@ -48,7 +47,7 @@ def test_success_bls_change(spec, state): assert post_credentials[12:] == signed_address_change.message.to_execution_address -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_success_exit_and_bls_change(spec, state): # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit @@ -77,7 +76,7 @@ def test_success_exit_and_bls_change(spec, state): assert spec.is_fully_withdrawable_validator(validator, balance, validator.withdrawable_epoch) -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_duplicate_bls_changes_same_block(spec, state): index = 0 @@ -96,7 +95,7 @@ def test_invalid_duplicate_bls_changes_same_block(spec, state): yield 'post', None -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_block(spec, state): index = 0 @@ -124,7 +123,7 @@ def test_invalid_two_bls_changes_of_different_addresses_same_validator_same_bloc # Withdrawals # -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_full_withdrawal_in_epoch_transition(spec, state): index = 0 @@ -145,7 +144,7 @@ def test_full_withdrawal_in_epoch_transition(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_partial_withdrawal_in_epoch_transition(spec, state): index = state.next_withdrawal_index @@ -169,7 +168,7 @@ def test_partial_withdrawal_in_epoch_transition(spec, state): assert len(spec.get_expected_withdrawals(state)) == 0 -@with_phases([CAPELLA]) +@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 @@ -221,7 +220,7 @@ def _perform_valid_withdrawal(spec, state): return pre_state, signed_block_1, pre_next_withdrawal_index -@with_phases([CAPELLA]) +@with_capella_and_later @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) @@ -238,7 +237,7 @@ def test_withdrawal_success_two_blocks(spec, state): yield 'post', state -@with_phases([CAPELLA]) +@with_capella_and_later @spec_state_test def test_invalid_withdrawal_fail_second_block_payload_isnt_compatible(spec, state): _perform_valid_withdrawal(spec, state) 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 deleted file mode 100644 index d9b93394f..000000000 --- a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_bls_to_execution_change.py +++ /dev/null @@ -1,40 +0,0 @@ -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 deleted file mode 100644 index a7db37e42..000000000 --- a/tests/core/pyspec/eth2spec/test/eip4844/block_processing/test_process_withdrawals.py +++ /dev/null @@ -1,41 +0,0 @@ - -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/unittests/validator/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/__init__.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/__init__.py rename to tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/__init__.py diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/test_validate_blobs_sidecar.py similarity index 100% rename from tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py rename to tests/core/pyspec/eth2spec/test/eip4844/unittests/fork_choice/test_validate_blobs_sidecar.py diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index ed61f8bdb..44b42aff9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -1,5 +1,8 @@ -from eth2spec.test.helpers.forks import is_post_altair +from eth2spec.test.helpers.forks import ( + is_post_altair, + is_post_capella, +) def get_process_calls(spec): @@ -22,7 +25,10 @@ def get_process_calls(spec): 'process_effective_balance_updates', 'process_slashings_reset', 'process_randao_mixes_reset', - 'process_historical_roots_update', + # Capella replaced `process_historical_roots_update` with `process_historical_summaries_update` + 'process_historical_summaries_update' if is_post_capella(spec) else ( + 'process_historical_roots_update' + ), # Altair replaced `process_participation_record_updates` with `process_participation_flag_updates` 'process_participation_flag_updates' if is_post_altair(spec) else ( 'process_participation_record_updates' diff --git a/tests/core/pyspec/eth2spec/test/helpers/light_client.py b/tests/core/pyspec/eth2spec/test/helpers/light_client.py index 8d632b3a1..215d174fc 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/light_client.py +++ b/tests/core/pyspec/eth2spec/test/helpers/light_client.py @@ -5,28 +5,7 @@ from eth2spec.test.helpers.sync_committee import ( compute_aggregate_sync_committee_signature, compute_committee_indices, ) - - -def signed_block_to_header(spec, block): - return spec.BeaconBlockHeader( - slot=block.message.slot, - proposer_index=block.message.proposer_index, - parent_root=block.message.parent_root, - state_root=block.message.state_root, - body_root=block.message.body.hash_tree_root(), - ) - - -def initialize_light_client_store(spec, state): - return spec.LightClientStore( - finalized_header=spec.BeaconBlockHeader(), - current_sync_committee=state.current_sync_committee, - next_sync_committee=state.next_sync_committee, - best_valid_update=None, - optimistic_header=spec.BeaconBlockHeader(), - previous_max_active_participants=0, - current_max_active_participants=0, - ) +from math import floor def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): @@ -60,3 +39,32 @@ def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): sync_committee_signature=sync_committee_signature, ) return sync_aggregate, signature_slot + + +def create_update(spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate): + num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) + + update = spec.LightClientUpdate() + + update.attested_header = spec.block_to_light_client_header(attested_block) + + if with_next: + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = spec.compute_merkle_proof_for_state( + attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) + + if with_finality: + update.finalized_header = spec.block_to_light_client_header(finalized_block) + update.finality_branch = spec.compute_merkle_proof_for_state( + attested_state, spec.FINALIZED_ROOT_INDEX) + + update.sync_aggregate, update.signature_slot = get_sync_aggregate( + spec, attested_state, num_participants) + + return update diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py index 02ce7ccba..f03d0bd98 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_historical_roots_update.py @@ -1,4 +1,8 @@ -from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.context import ( + PHASE0, ALTAIR, BELLATRIX, + spec_state_test, + with_phases, +) from eth2spec.test.helpers.epoch_processing import ( run_epoch_processing_with ) @@ -8,7 +12,7 @@ def run_process_historical_roots_update(spec, state): yield from run_epoch_processing_with(spec, state, 'process_historical_roots_update') -@with_all_phases +@with_phases([PHASE0, ALTAIR, BELLATRIX]) @spec_state_test def test_historical_root_accumulator(spec, state): # skip ahead to near the end of the historical roots period (excl block before epoch processing) 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 1e9dd2d48..2e1a2a369 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -30,7 +30,7 @@ from eth2spec.test.helpers.sync_committee import ( compute_sync_committee_participant_reward_and_penalty, ) from eth2spec.test.helpers.constants import PHASE0, MINIMAL -from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix +from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix, is_post_capella from eth2spec.test.context import ( spec_test, spec_state_test, dump_skipping_message, with_phases, with_all_phases, single_phase, @@ -1026,7 +1026,10 @@ def test_balance_driven_status_transitions(spec, state): @always_bls def test_historical_batch(spec, state): state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 - pre_historical_roots_len = len(state.historical_roots) + pre_historical_roots = state.historical_roots.copy() + + if is_post_capella(spec): + pre_historical_summaries = state.historical_summaries.copy() yield 'pre', state @@ -1038,7 +1041,14 @@ def test_historical_batch(spec, state): assert state.slot == block.slot assert spec.get_current_epoch(state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0 - assert len(state.historical_roots) == pre_historical_roots_len + 1 + + # check history update + if is_post_capella(spec): + # Frozen `historical_roots` + assert state.historical_roots == pre_historical_roots + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + else: + assert len(state.historical_roots) == len(pre_historical_roots) + 1 @with_all_phases diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py index 3fa57f0f1..90d332d57 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_slots.py @@ -1,3 +1,6 @@ +from eth2spec.test.helpers.forks import ( + is_post_capella, +) from eth2spec.test.helpers.state import get_state_root from eth2spec.test.context import ( spec_state_test, @@ -61,3 +64,26 @@ def test_over_epoch_boundary(spec, state): yield 'slots', int(slots) spec.process_slots(state, state.slot + slots) yield 'post', state + + +@with_all_phases +@spec_state_test +def test_historical_accumulator(spec, state): + pre_historical_roots = state.historical_roots.copy() + + if is_post_capella(spec): + pre_historical_summaries = state.historical_summaries.copy() + + yield 'pre', state + slots = spec.SLOTS_PER_HISTORICAL_ROOT + yield 'slots', int(slots) + spec.process_slots(state, state.slot + slots) + yield 'post', state + + # check history update + if is_post_capella(spec): + # Frozen `historical_roots` + assert state.historical_roots == pre_historical_roots + assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 + else: + assert len(state.historical_roots) == len(pre_historical_roots) + 1 diff --git a/tests/formats/epoch_processing/README.md b/tests/formats/epoch_processing/README.md index 41d2a102a..2951767f2 100644 --- a/tests/formats/epoch_processing/README.md +++ b/tests/formats/epoch_processing/README.md @@ -41,11 +41,10 @@ Sub-transitions: - `effective_balance_updates` - `slashings_reset` - `randao_mixes_reset` -- `historical_roots_update` +- `historical_roots_update` (Phase0, Altair, Bellatrix only) +- `historical_summaries_update` (Capella) - `participation_record_updates` (Phase 0 only) - `participation_flag_updates` (Altair) - `sync_committee_updates` (Altair) -- `full_withdrawals` (Capella) -- `partial_withdrawals` (Capella) The resulting state should match the expected `post` state. diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index ba2c64b3a..810d62578 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -33,17 +33,18 @@ This excludes the other parts of the block-transition. Operations: -| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | -|-------------------------|-----------------------|----------------------|----------------------------------------------------------------------| -| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | -| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | -| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | -| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | -| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | -| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | -| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | -| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | -| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `signed_address_change` | `process_bls_to_execution_change(state, signed_address_change)` (new in Capella) | +| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* | +|---------------------------|------------------------------|---------------------|----------------------------------------------------------------------------------| +| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` | +| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` | +| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` | +| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` | +| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | +| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | +| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | +| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | +| `withdrawals` | `ExecutionPayload` | `execution_payload` | `process_withdrawals(state, execution_payload)` (new in Capella) | +| `bls_to_execution_change` | `SignedBLSToExecutionChange` | `address_change` | `process_bls_to_execution_change(state, address_change)` (new in Capella) | Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py index 84421e749..dda4345a8 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -27,12 +27,11 @@ if __name__ == "__main__": # so no additional tests required. bellatrix_mods = altair_mods - # No epoch-processing changes in Capella and previous testing repeats with new types, - # so no additional tests required. - capella_mods = bellatrix_mods + _new_capella_mods = {key: 'eth2spec.test.capella.epoch_processing.test_process_' + key for key in [ + 'historical_summaries_update', + ]} + capella_mods = combine_mods(_new_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 diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index 4c073ebe4..d370a1b85 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -36,11 +36,7 @@ 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) + 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 [