diff --git a/.circleci/config.yml b/.circleci/config.yml index 665207bdd..5958a2fc6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ commands: jobs: checkout_specs: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: # Restore git repo at point close to target branch/revision, to speed up checkout @@ -80,7 +80,7 @@ jobs: - ~/specs-repo install_pyspec_test: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -92,7 +92,7 @@ jobs: - save_pyspec_cached_venv test-phase0: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -105,7 +105,7 @@ jobs: path: tests/core/pyspec/test-reports test-altair: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -118,7 +118,7 @@ jobs: path: tests/core/pyspec/test-reports test-bellatrix: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -131,7 +131,7 @@ jobs: path: tests/core/pyspec/test-reports test-capella: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -144,7 +144,7 @@ jobs: path: tests/core/pyspec/test-reports test-deneb: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -155,6 +155,19 @@ jobs: command: make citest fork=deneb - store_test_results: path: tests/core/pyspec/test-reports + test-eip6110: + docker: + - image: circleci/python:3.8 + working_directory: ~/specs-repo + steps: + - restore_cache: + key: v3-specs-repo-{{ .Branch }}-{{ .Revision }} + - restore_pyspec_cached_venv + - run: + name: Run py-tests + command: make citest fork=eip6110 + - store_test_results: + path: tests/core/pyspec/test-reports table_of_contents: docker: - image: circleci/node:10.16.3 @@ -166,7 +179,7 @@ jobs: command: sudo npm install -g doctoc@2 && make check_toc codespell: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - checkout @@ -175,7 +188,7 @@ jobs: command: pip install 'codespell<3.0.0,>=2.0.0' --user && make codespell lint: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -231,7 +244,7 @@ jobs: - /nix install_deposit_contract_web3_tester: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -243,7 +256,7 @@ jobs: - save_deposit_contract_tester_cached_venv test_deposit_contract_web3_tests: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/specs-repo steps: - restore_cache: @@ -275,6 +288,9 @@ workflows: - test-deneb: requires: - install_pyspec_test + - test-eip6110: + requires: + - install_pyspec_test - table_of_contents - codespell - lint: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 926c3fbbf..41a80ab92 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -83,7 +83,7 @@ jobs: needs: [preclear,lint,codespell,table_of_contents] strategy: matrix: - version: ["phase0", "altair", "bellatrix", "capella", "deneb"] + version: ["phase0", "altair", "bellatrix", "capella", "deneb", "eip6110"] steps: - name: Checkout this repo uses: actions/checkout@v3.2.0 diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index f204d1746..5ad394c08 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -50,8 +50,9 @@ CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC # Deneb DENEB_FORK_VERSION: 0x04000000 DENEB_FORK_EPOCH: 18446744073709551615 - - +# EIP6110 +EIP6110_FORK_VERSION: 0x05000000 # temporary stub +EIP6110_FORK_EPOCH: 18446744073709551615 # Time parameters diff --git a/configs/minimal.yaml b/configs/minimal.yaml index abecb1881..5895cfc70 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -49,6 +49,9 @@ CAPELLA_FORK_EPOCH: 18446744073709551615 # DENEB DENEB_FORK_VERSION: 0x04000001 DENEB_FORK_EPOCH: 18446744073709551615 +# EIP6110 +EIP6110_FORK_VERSION: 0x05000001 +EIP6110_FORK_EPOCH: 18446744073709551615 # Time parameters diff --git a/presets/mainnet/eip6110.yaml b/presets/mainnet/eip6110.yaml new file mode 100644 index 000000000..16bf787d0 --- /dev/null +++ b/presets/mainnet/eip6110.yaml @@ -0,0 +1,6 @@ +# Mainnet preset - EIP6110 + +# Execution +# --------------------------------------------------------------- +# 2**13 (= 8192) receipts +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192 diff --git a/presets/minimal/eip6110.yaml b/presets/minimal/eip6110.yaml new file mode 100644 index 000000000..7486aa16e --- /dev/null +++ b/presets/minimal/eip6110.yaml @@ -0,0 +1,6 @@ +# Minimal preset - EIP6110 + +# Execution +# --------------------------------------------------------------- +# [customized] +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4 diff --git a/setup.py b/setup.py index fc3acb806..5d2736979 100644 --- a/setup.py +++ b/setup.py @@ -671,13 +671,13 @@ def retrieve_blobs_and_proofs(beacon_block_root: Root) -> PyUnion[Tuple[Blob, KZ # # EIP6110SpecBuilder # -class EIP6110SpecBuilder(CapellaSpecBuilder): +class EIP6110SpecBuilder(DenebSpecBuilder): fork: str = EIP6110 @classmethod def imports(cls, preset_name: str): return super().imports(preset_name) + f''' -from eth2spec.capella import {preset_name} as capella +from eth2spec.deneb import {preset_name} as deneb ''' @@ -1022,7 +1022,7 @@ class PySpecCommand(Command): specs/capella/validator.md specs/capella/p2p-interface.md """ - if self.spec_fork == DENEB: + if self.spec_fork in (DENEB, EIP6110): self.md_doc_paths += """ specs/deneb/light-client/fork.md specs/deneb/light-client/full-node.md @@ -1037,6 +1037,10 @@ class PySpecCommand(Command): """ if self.spec_fork == EIP6110: self.md_doc_paths += """ + specs/_features/eip6110/light-client/fork.md + specs/_features/eip6110/light-client/full-node.md + specs/_features/eip6110/light-client/p2p-interface.md + specs/_features/eip6110/light-client/sync-protocol.md specs/_features/eip6110/beacon-chain.md specs/_features/eip6110/fork.md """ @@ -1176,7 +1180,7 @@ setup( packages=find_packages(where='tests/core/pyspec') + ['configs', 'specs'], py_modules=["eth2spec"], cmdclass=commands, - python_requires=">=3.8, <4", + python_requires=">=3.9, <4", extras_require={ "test": ["pytest>=4.4", "pytest-cov", "pytest-xdist"], "lint": ["flake8==5.0.4", "mypy==0.981", "pylint==2.15.3"], diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 01999a929..708418e1c 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -33,7 +33,7 @@ This is the beacon chain specification of in-protocol deposits processing mechanism. This mechanism relies on the changes proposed by [EIP-6110](http://eips.ethereum.org/EIPS/eip-6110). -*Note:* This specification is built upon [Capella](../../capella/beacon-chain.md) and is under active development. +*Note:* This specification is built upon [Deneb](../../deneb/beacon-chain.md) and is under active development. ## Constants @@ -91,7 +91,8 @@ class ExecutionPayload(Container): block_hash: Hash32 transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] - deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in EIP-6110] + excess_data_gas: uint256 + deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in EIP6110] ``` #### `ExecutionPayloadHeader` @@ -115,7 +116,8 @@ class ExecutionPayloadHeader(Container): block_hash: Hash32 transactions_root: Root withdrawals_root: Root - deposit_receipts_root: Root # [New in EIP-6110] + excess_data_gas: uint256 + deposit_receipts_root: Root # [New in EIP6110] ``` #### `BeaconState` @@ -157,13 +159,13 @@ class BeaconState(Container): current_sync_committee: SyncCommittee next_sync_committee: SyncCommittee # Execution - latest_execution_payload_header: ExecutionPayloadHeader + latest_execution_payload_header: ExecutionPayloadHeader # [Modified in EIP6110] # Withdrawals next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex # Deep history valid from Capella onwards historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] - # [New in EIP-6110] + # [New in EIP6110] deposit_receipts_start_index: uint64 ``` @@ -176,11 +178,12 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP-6110] + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP6110] process_randao(state, block.body) process_eth1_data(state, block.body) - process_operations(state, block.body) # [Modified in EIP-6110] + process_operations(state, block.body) # [Modified in EIP6110] process_sync_aggregate(state, block.body.sync_aggregate) + process_blob_kzg_commitments(block.body) ``` #### Modified `process_operations` @@ -189,7 +192,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - # [Modified in EIP-6110] + # [Modified in EIP6110] # Disable former deposit mechanism once all prior deposits are processed eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_receipts_start_index) if state.eth1_deposit_index < eth1_deposit_index_limit: @@ -204,11 +207,11 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: for_ops(body.proposer_slashings, process_proposer_slashing) for_ops(body.attester_slashings, process_attester_slashing) for_ops(body.attestations, process_attestation) - for_ops(body.deposits, process_deposit) # [Modified in EIP-6110] + for_ops(body.deposits, process_deposit) for_ops(body.voluntary_exits, process_voluntary_exit) for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) - # [New in EIP-6110] + # [New in EIP6110] if is_execution_enabled(state, body): for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) ``` @@ -262,7 +265,8 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe block_hash=payload.block_hash, transactions_root=hash_tree_root(payload.transactions), withdrawals_root=hash_tree_root(payload.withdrawals), - deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in EIP-6110] + excess_data_gas=payload.excess_data_gas, + deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [New in EIP6110] ) ``` diff --git a/specs/_features/eip6110/fork.md b/specs/_features/eip6110/fork.md index b08661e5f..2145a9d1a 100644 --- a/specs/_features/eip6110/fork.md +++ b/specs/_features/eip6110/fork.md @@ -43,7 +43,9 @@ def compute_fork_version(epoch: Epoch) -> Version: Return the fork version at the given ``epoch``. """ if epoch >= EIP6110_FORK_EPOCH: - return EIP6110_FORK_EPOCH + return EIP6110_FORK_VERSION + if epoch >= DENEB_FORK_EPOCH: + return DENEB_FORK_VERSION if epoch >= CAPELLA_FORK_EPOCH: return CAPELLA_FORK_VERSION if epoch >= BELLATRIX_FORK_EPOCH: @@ -68,8 +70,8 @@ If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == an irregular state change is made to upgrade to EIP-6110. ```python -def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState: - epoch = capella.get_current_epoch(pre) +def upgrade_to_eip6110(pre: deneb.BeaconState) -> BeaconState: + epoch = deneb.get_current_epoch(pre) latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=pre.latest_execution_payload_header.parent_hash, fee_recipient=pre.latest_execution_payload_header.fee_recipient, @@ -86,6 +88,7 @@ def upgrade_to_eip6110(pre: capella.BeaconState) -> BeaconState: block_hash=pre.latest_execution_payload_header.block_hash, transactions_root=pre.latest_execution_payload_header.transactions_root, withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, + excess_data_gas=uint256(0), deposit_receipts_root=Root(), # [New in EIP-6110] ) post = BeaconState( diff --git a/specs/_features/eip6110/light-client/fork.md b/specs/_features/eip6110/light-client/fork.md new file mode 100644 index 000000000..34f0fef8c --- /dev/null +++ b/specs/_features/eip6110/light-client/fork.md @@ -0,0 +1,112 @@ +# EIP-6110 Light Client -- Fork Logic + +## Table of contents + + + + + +- [Introduction](#introduction) + - [Upgrading light client data](#upgrading-light-client-data) + - [Upgrading the store](#upgrading-the-store) + + + + +## Introduction + +This document describes how to upgrade existing light client objects based on the [Deneb specification](../../deneb/light-client/sync-protocol.md) to eip6110. This is necessary when processing pre-eip6110 data with a post-eip6110 `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format. + +### Upgrading light client data + +A eip6110 `LightClientStore` can still process earlier light client data. In order to do so, that pre-eip6110 data needs to be locally upgraded to eip6110 before processing. + +```python +def upgrade_lc_header_to_eip6110(pre: deneb.LightClientHeader) -> LightClientHeader: + return LightClientHeader( + beacon=pre.beacon, + execution=ExecutionPayloadHeader( + parent_hash=pre.execution.parent_hash, + fee_recipient=pre.execution.fee_recipient, + state_root=pre.execution.state_root, + receipts_root=pre.execution.receipts_root, + logs_bloom=pre.execution.logs_bloom, + prev_randao=pre.execution.prev_randao, + block_number=pre.execution.block_number, + gas_limit=pre.execution.gas_limit, + gas_used=pre.execution.gas_used, + timestamp=pre.execution.timestamp, + extra_data=pre.execution.extra_data, + base_fee_per_gas=pre.execution.base_fee_per_gas, + block_hash=pre.execution.block_hash, + transactions_root=pre.execution.transactions_root, + withdrawals_root=pre.execution.withdrawals_root, + excess_data_gas=pre.execution.excess_data_gas, + deposit_receipts_root=Root(), # [New in EIP6110] + ), + execution_branch=pre.execution_branch, + ) +``` + +```python +def upgrade_lc_bootstrap_to_eip6110(pre: deneb.LightClientBootstrap) -> LightClientBootstrap: + return LightClientBootstrap( + header=upgrade_lc_header_to_eip6110(pre.header), + current_sync_committee=pre.current_sync_committee, + current_sync_committee_branch=pre.current_sync_committee_branch, + ) +``` + +```python +def upgrade_lc_update_to_eip6110(pre: deneb.LightClientUpdate) -> LightClientUpdate: + return LightClientUpdate( + attested_header=upgrade_lc_header_to_eip6110(pre.attested_header), + next_sync_committee=pre.next_sync_committee, + next_sync_committee_branch=pre.next_sync_committee_branch, + finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header), + finality_branch=pre.finality_branch, + sync_aggregate=pre.sync_aggregate, + signature_slot=pre.signature_slot, + ) +``` + +```python +def upgrade_lc_finality_update_to_eip6110(pre: deneb.LightClientFinalityUpdate) -> LightClientFinalityUpdate: + return LightClientFinalityUpdate( + attested_header=upgrade_lc_header_to_eip6110(pre.attested_header), + finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header), + finality_branch=pre.finality_branch, + sync_aggregate=pre.sync_aggregate, + signature_slot=pre.signature_slot, + ) +``` + +```python +def upgrade_lc_optimistic_update_to_eip6110(pre: deneb.LightClientOptimisticUpdate) -> LightClientOptimisticUpdate: + return LightClientOptimisticUpdate( + attested_header=upgrade_lc_header_to_eip6110(pre.attested_header), + sync_aggregate=pre.sync_aggregate, + signature_slot=pre.signature_slot, + ) +``` + +### Upgrading the store + +Existing `LightClientStore` objects based on Deneb MUST be upgraded to eip6110 before eip6110 based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `EIP6110_FORK_EPOCH`. + +```python +def upgrade_lc_store_to_eip6110(pre: deneb.LightClientStore) -> LightClientStore: + if pre.best_valid_update is None: + best_valid_update = None + else: + best_valid_update = upgrade_lc_update_to_eip6110(pre.best_valid_update) + return LightClientStore( + finalized_header=upgrade_lc_header_to_eip6110(pre.finalized_header), + current_sync_committee=pre.current_sync_committee, + next_sync_committee=pre.next_sync_committee, + best_valid_update=best_valid_update, + optimistic_header=upgrade_lc_header_to_eip6110(pre.optimistic_header), + previous_max_active_participants=pre.previous_max_active_participants, + current_max_active_participants=pre.current_max_active_participants, + ) +``` diff --git a/specs/_features/eip6110/light-client/full-node.md b/specs/_features/eip6110/light-client/full-node.md new file mode 100644 index 000000000..03c0f17bd --- /dev/null +++ b/specs/_features/eip6110/light-client/full-node.md @@ -0,0 +1,77 @@ +# EIP-6110 Light Client -- Full Node + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Helper functions](#helper-functions) + - [Modified `block_to_light_client_header`](#modified-block_to_light_client_header) + + + + +## Introduction + +This upgrade adds information about the execution payload to light client data as part of the EIP-6110 upgrade. + +## Helper functions + +### Modified `block_to_light_client_header` + +```python +def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader: + epoch = compute_epoch_at_slot(block.message.slot) + + if epoch >= CAPELLA_FORK_EPOCH: + payload = block.message.body.execution_payload + execution_header = ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + ) + + if epoch >= DENEB_FORK_EPOCH: + execution_header.excess_data_gas = payload.excess_data_gas + + # [New in EIP6110] + if epoch >= EIP6110_FORK_EPOCH: + execution_header.deposit_receipts_root = hash_tree_root(payload.deposit_receipts) + + execution_branch = compute_merkle_proof_for_block_body(block.message.body, EXECUTION_PAYLOAD_INDEX) + else: + # Note that during fork transitions, `finalized_header` may still point to earlier forks. + # While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`), + # it was not included in the corresponding light client data. To ensure compatibility + # with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data. + execution_header = ExecutionPayloadHeader() + execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))] + + return LightClientHeader( + beacon=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), + ), + execution=execution_header, + execution_branch=execution_branch, + ) +``` diff --git a/specs/_features/eip6110/light-client/p2p-interface.md b/specs/_features/eip6110/light-client/p2p-interface.md new file mode 100644 index 000000000..f55fb2f77 --- /dev/null +++ b/specs/_features/eip6110/light-client/p2p-interface.md @@ -0,0 +1,111 @@ +# EIP-6110 Light Client -- Networking + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [Networking](#networking) + - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Global topics](#global-topics) + - [`light_client_finality_update`](#light_client_finality_update) + - [`light_client_optimistic_update`](#light_client_optimistic_update) + - [The Req/Resp domain](#the-reqresp-domain) + - [Messages](#messages) + - [GetLightClientBootstrap](#getlightclientbootstrap) + - [LightClientUpdatesByRange](#lightclientupdatesbyrange) + - [GetLightClientFinalityUpdate](#getlightclientfinalityupdate) + - [GetLightClientOptimisticUpdate](#getlightclientoptimisticupdate) + + + + +## Networking + +The [Deneb light client networking specification](../../deneb/light-client/p2p-interface.md) is extended to exchange [EIP-6110 light client data](./sync-protocol.md). + +### The gossip domain: gossipsub + +#### Topics and messages + +##### Global topics + +###### `light_client_finality_update` + +[0]: # (eth2spec: skip) + +| `fork_version` | Message SSZ type | +|--------------------------------------------------------|-------------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` | +| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientFinalityUpdate` | + +###### `light_client_optimistic_update` + +[0]: # (eth2spec: skip) + +| `fork_version` | Message SSZ type | +|--------------------------------------------------------|---------------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` | +| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientOptimisticUpdate` | + +### The Req/Resp domain + +#### Messages + +##### GetLightClientBootstrap + +[0]: # (eth2spec: skip) + +| `fork_version` | Response SSZ type | +|--------------------------------------------------------|------------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientBootstrap` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientBootstrap` | +| `DENEB_FORK_VERSION` | `deneb.LightClientBootstrap` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientBootstrap` | + +##### LightClientUpdatesByRange + +[0]: # (eth2spec: skip) + +| `fork_version` | Response chunk SSZ type | +|--------------------------------------------------------|----------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientUpdate` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientUpdate` | +| `DENEB_FORK_VERSION` | `deneb.LightClientUpdate` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientUpdate` | + +##### GetLightClientFinalityUpdate + +[0]: # (eth2spec: skip) + +| `fork_version` | Response SSZ type | +|--------------------------------------------------------|-------------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` | +| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientFinalityUpdate` | + +##### GetLightClientOptimisticUpdate + +[0]: # (eth2spec: skip) + +| `fork_version` | Response SSZ type | +|--------------------------------------------------------|---------------------------------------| +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` | +| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` | +| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` | +| `EIP6110_FORK_VERSION` and later | `eip6110.LightClientOptimisticUpdate` | diff --git a/specs/_features/eip6110/light-client/sync-protocol.md b/specs/_features/eip6110/light-client/sync-protocol.md new file mode 100644 index 000000000..bcb9d50e4 --- /dev/null +++ b/specs/_features/eip6110/light-client/sync-protocol.md @@ -0,0 +1,89 @@ +# EIP-6110 Light Client -- Sync Protocol + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Helper functions](#helper-functions) + - [Modified `get_lc_execution_root`](#modified-get_lc_execution_root) + - [Modified `is_valid_light_client_header`](#modified-is_valid_light_client_header) + + + + +## Introduction + +This upgrade updates light client data to include the EIP-6110 changes to the [`ExecutionPayload`](../beacon-chain.md) structure. It extends the [Deneb Light Client specifications](../../deneb/light-client/sync-protocol.md). The [fork document](./fork.md) explains how to upgrade existing Deneb based deployments to EIP-6110. + +Additional documents describes the impact of the upgrade on certain roles: +- [Full node](./full-node.md) +- [Networking](./p2p-interface.md) + +## Helper functions + +### Modified `get_lc_execution_root` + +```python +def get_lc_execution_root(header: LightClientHeader) -> Root: + epoch = compute_epoch_at_slot(header.beacon.slot) + + if epoch >= DENEB_FORK_EPOCH: + return hash_tree_root(header.execution) + + if epoch >= CAPELLA_FORK_EPOCH: + execution_header = capella.ExecutionPayloadHeader( + parent_hash=header.execution.parent_hash, + fee_recipient=header.execution.fee_recipient, + state_root=header.execution.state_root, + receipts_root=header.execution.receipts_root, + logs_bloom=header.execution.logs_bloom, + prev_randao=header.execution.prev_randao, + block_number=header.execution.block_number, + gas_limit=header.execution.gas_limit, + gas_used=header.execution.gas_used, + timestamp=header.execution.timestamp, + extra_data=header.execution.extra_data, + base_fee_per_gas=header.execution.base_fee_per_gas, + block_hash=header.execution.block_hash, + transactions_root=header.execution.transactions_root, + withdrawals_root=header.execution.withdrawals_root, + ) + return hash_tree_root(execution_header) + + return Root() +``` + +### Modified `is_valid_light_client_header` + +```python +def is_valid_light_client_header(header: LightClientHeader) -> bool: + epoch = compute_epoch_at_slot(header.beacon.slot) + + # [New in EIP-6110] + if epoch < EIP6110_FORK_EPOCH: + if header.execution.deposit_receipts_root != Root(): + return False + + if epoch < DENEB_FORK_EPOCH: + if header.execution.excess_data_gas != uint256(0): + return False + + if epoch < CAPELLA_FORK_EPOCH: + return ( + header.execution == ExecutionPayloadHeader() + and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))] + ) + + return is_valid_merkle_branch( + leaf=get_lc_execution_root(header), + branch=header.execution_branch, + depth=floorlog2(EXECUTION_PAYLOAD_INDEX), + index=get_subtree_index(EXECUTION_PAYLOAD_INDEX), + root=header.beacon.body_root, + ) +``` diff --git a/specs/_features/eip6110/validator.md b/specs/_features/eip6110/validator.md index ae9d493a6..6770ef56a 100644 --- a/specs/_features/eip6110/validator.md +++ b/specs/_features/eip6110/validator.md @@ -20,7 +20,7 @@ This document represents the changes to be made in the code of an "honest valida ## Prerequisites -This document is an extension of the [Capella -- Honest Validator](../../capella/validator.md) guide. +This document is an extension of the [Deneb -- Honest Validator](../../deneb/validator.md) guide. All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [EIP-6110](./beacon-chain.md) are requisite for this document and used throughout. diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index ed7d60a93..68519ff90 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -170,7 +170,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: 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 + finalized_checkpoint_block = get_checkpoint_block( + store, + block.parent_root, + store.finalized_checkpoint.epoch, + ) + assert store.finalized_checkpoint.root == finalized_checkpoint_block # Check the block is valid and compute the post-state state = pre_state.copy() diff --git a/specs/deneb/fork-choice.md b/specs/deneb/fork-choice.md index 61714cf1a..9faa11077 100644 --- a/specs/deneb/fork-choice.md +++ b/specs/deneb/fork-choice.md @@ -82,7 +82,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: 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 + finalized_checkpoint_block = get_checkpoint_block( + store, + block.parent_root, + store.finalized_checkpoint.epoch, + ) + assert store.finalized_checkpoint.root == finalized_checkpoint_block # [New in Deneb] # Check if blob data is available diff --git a/specs/deneb/light-client/fork.md b/specs/deneb/light-client/fork.md index 8c552937a..46a093028 100644 --- a/specs/deneb/light-client/fork.md +++ b/specs/deneb/light-client/fork.md @@ -41,6 +41,7 @@ def upgrade_lc_header_to_deneb(pre: capella.LightClientHeader) -> LightClientHea block_hash=pre.execution.block_hash, transactions_root=pre.execution.transactions_root, withdrawals_root=pre.execution.withdrawals_root, + excess_data_gas=uint256(0), # [New in Deneb] ), execution_branch=pre.execution_branch, ) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index f39a9234c..be6edca64 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -18,6 +18,7 @@ - [`get_current_slot`](#get_current_slot) - [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start) - [`get_ancestor`](#get_ancestor) + - [`get_checkpoint_block`](#get_checkpoint_block) - [`get_weight`](#get_weight) - [`get_voting_source`](#get_voting_source) - [`filter_block_tree`](#filter_block_tree) @@ -190,6 +191,17 @@ def get_ancestor(store: Store, root: Root, slot: Slot) -> Root: return root ``` +#### `get_checkpoint_block` + +```python +def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root: + """ + Compute the checkpoint block for epoch ``epoch`` in the chain of block ``root`` + """ + epoch_first_slot = compute_start_slot_at_epoch(epoch) + return get_ancestor(store, root, epoch_first_slot) +``` + #### `get_weight` ```python @@ -276,10 +288,15 @@ def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconB voting_source.epoch + 2 >= current_epoch ) - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + finalized_checkpoint_block = get_checkpoint_block( + store, + block_root, + store.finalized_checkpoint.epoch, + ) + correct_finalized = ( store.finalized_checkpoint.epoch == GENESIS_EPOCH - or store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot) + or store.finalized_checkpoint.root == finalized_checkpoint_block ) # If expected finalized/justified, add to viable block-tree and signal viability to parent. @@ -440,8 +457,7 @@ def validate_on_attestation(store: Store, attestation: Attestation, is_from_bloc assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot # LMD vote must be consistent with FFG vote target - target_slot = compute_start_slot_at_epoch(target.epoch) - assert target.root == get_ancestor(store, attestation.data.beacon_block_root, target_slot) + assert target.root == get_checkpoint_block(store, attestation.data.beacon_block_root, target.epoch) # Attestations can only affect the fork choice of subsequent slots. # Delay consideration in the fork choice until their slot is in the past. @@ -504,7 +520,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: 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 + finalized_checkpoint_block = get_checkpoint_block( + store, + block.parent_root, + store.finalized_checkpoint.epoch, + ) + assert store.finalized_checkpoint.root == finalized_checkpoint_block # Check the block is valid and compute the post-state state = pre_state.copy() diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index f38601ed8..2503d906c 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -317,7 +317,7 @@ The following validations MUST pass before forwarding the `signed_beacon_block` - _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation. - _[REJECT]_ The block is from a higher slot than its parent. - _[REJECT]_ The current `finalized_checkpoint` is an ancestor of `block` -- i.e. - `get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + `get_checkpoint_block(store, block.parent_root, store.finalized_checkpoint.epoch) == store.finalized_checkpoint.root` - _[REJECT]_ The block is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`). @@ -356,7 +356,7 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ (a client MAY queue aggregates for processing once block is retrieved). - _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation. - _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e. - `get_ancestor(store, aggregate.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + `get_checkpoint_block(store, aggregate.data.beacon_block_root, finalized_checkpoint.epoch) == store.finalized_checkpoint.root` @@ -425,9 +425,9 @@ The following validations MUST pass before forwarding the `attestation` on the s (a client MAY queue attestations for processing once block is retrieved). - _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation. - _[REJECT]_ The attestation's target block is an ancestor of the block named in the LMD vote -- i.e. - `get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(attestation.data.target.epoch)) == attestation.data.target.root` + `get_checkpoint_block(store, attestation.data.beacon_block_root, attestation.data.target.epoch) == attestation.data.target.root` - _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e. - `get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) + `get_checkpoint_block(store, attestation.data.beacon_block_root, store.finalized_checkpoint.epoch) == store.finalized_checkpoint.root` diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py index d33e68961..1a527a767 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py @@ -26,6 +26,7 @@ from eth2spec.test.helpers.fork_transition import ( from eth2spec.test.helpers.forks import ( is_post_capella, is_post_deneb, is_post_fork, + is_post_eip6110, ) from eth2spec.test.helpers.light_client import ( get_sync_aggregate, @@ -57,6 +58,10 @@ def needs_upgrade_to_deneb(d_spec, s_spec): return is_post_deneb(s_spec) and not is_post_deneb(d_spec) +def needs_upgrade_to_eip6110(d_spec, s_spec): + return is_post_eip6110(s_spec) and not is_post_eip6110(d_spec) + + def check_lc_header_equal(d_spec, s_spec, data, upgraded): assert upgraded.beacon.slot == data.beacon.slot assert upgraded.beacon.hash_tree_root() == data.beacon.hash_tree_root() @@ -84,6 +89,10 @@ def upgrade_lc_bootstrap_to_store(d_spec, s_spec, data): upgraded = s_spec.upgrade_lc_bootstrap_to_deneb(upgraded) check_lc_bootstrap_equal(d_spec, s_spec, data, upgraded) + if needs_upgrade_to_eip6110(d_spec, s_spec): + upgraded = s_spec.upgrade_lc_bootstrap_to_eip6110(upgraded) + check_lc_bootstrap_equal(d_spec, s_spec, data, upgraded) + return upgraded @@ -145,6 +154,8 @@ class LightClientSyncTest(object): def get_store_fork_version(s_spec): + if is_post_eip6110(s_spec): + return s_spec.config.EIP6110_FORK_VERSION if is_post_deneb(s_spec): return s_spec.config.DENEB_FORK_VERSION if is_post_capella(s_spec): diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 38e7f0b71..901fd273a 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -8,11 +8,13 @@ from eth2spec.altair import mainnet as spec_altair_mainnet, minimal as spec_alta from eth2spec.bellatrix import mainnet as spec_bellatrix_mainnet, minimal as spec_bellatrix_minimal from eth2spec.capella import mainnet as spec_capella_mainnet, minimal as spec_capella_minimal from eth2spec.deneb import mainnet as spec_deneb_mainnet, minimal as spec_deneb_minimal +from eth2spec.eip6110 import mainnet as spec_eip6110_mainnet, minimal as spec_eip6110_minimal from eth2spec.utils import bls from .exceptions import SkippedTest from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, + EIP6110, MINIMAL, MAINNET, ALL_PHASES, ALL_FORK_UPGRADES, @@ -79,13 +81,15 @@ spec_targets: Dict[PresetBaseName, Dict[SpecForkName, Spec]] = { BELLATRIX: spec_bellatrix_minimal, CAPELLA: spec_capella_minimal, DENEB: spec_deneb_minimal, + EIP6110: spec_eip6110_minimal, }, MAINNET: { PHASE0: spec_phase0_mainnet, ALTAIR: spec_altair_mainnet, BELLATRIX: spec_bellatrix_mainnet, CAPELLA: spec_capella_mainnet, - DENEB: spec_deneb_mainnet + DENEB: spec_deneb_mainnet, + EIP6110: spec_eip6110_mainnet, }, } @@ -428,6 +432,7 @@ with_altair_and_later = with_all_phases_from(ALTAIR) with_bellatrix_and_later = with_all_phases_from(BELLATRIX) with_capella_and_later = with_all_phases_from(CAPELLA) with_deneb_and_later = with_all_phases_from(DENEB) +with_eip6110_and_later = with_all_phases_from(EIP6110) def _get_preset_targets(kw): diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index 7d89a9788..6d3f377a3 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -4,7 +4,8 @@ from eth2spec.test.context import ( spec_test, single_phase, with_deneb_and_later, - expect_assertion_error + expect_assertion_error, + always_bls ) from eth2spec.test.helpers.sharding import ( get_sample_blob, @@ -263,6 +264,7 @@ def test_validate_kzg_g1_neutral_element(spec): @with_deneb_and_later @spec_test @single_phase +@always_bls def test_validate_kzg_g1_not_in_g1(spec): """ Verify that `validate_kzg_g1` fails on point not in G1 @@ -274,6 +276,7 @@ def test_validate_kzg_g1_not_in_g1(spec): @with_deneb_and_later @spec_test @single_phase +@always_bls def test_validate_kzg_g1_not_on_curve(spec): """ Verify that `validate_kzg_g1` fails on point not in G1 diff --git a/tests/core/pyspec/eth2spec/test/eip6110/__init__.py b/tests/core/pyspec/eth2spec/test/eip6110/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip6110/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_deposit_receipt.py b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_deposit_receipt.py new file mode 100644 index 000000000..d78c18ecb --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip6110/block_processing/test_process_deposit_receipt.py @@ -0,0 +1,282 @@ +from eth2spec.test.context import spec_state_test, always_bls, with_eip6110_and_later +from eth2spec.test.helpers.deposits import ( + prepare_deposit_receipt, + run_deposit_receipt_processing, + run_deposit_receipt_processing_with_specific_fork_version +) +from eth2spec.test.helpers.state import next_epoch_via_block +from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable + + +@with_eip6110_and_later +@spec_state_test +def test_new_deposit_under_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. + amount = spec.MAX_EFFECTIVE_BALANCE - 1 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_new_deposit_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MAX_EFFECTIVE_BALANCE + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_new_deposit_over_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing + amount = spec.MAX_EFFECTIVE_BALANCE + 1 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_new_deposit_eth1_withdrawal_credentials(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + + b'\x00' * 11 # specified 0s + + b'\x59' * 20 # a 20-byte eth1 address + ) + amount = spec.MAX_EFFECTIVE_BALANCE + deposit_receipt = prepare_deposit_receipt( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_new_deposit_non_versioned_withdrawal_credentials(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + withdrawal_credentials = ( + b'\xFF' # Non specified withdrawal credentials version + + b'\x02' * 31 # Garabage bytes + ) + amount = spec.MAX_EFFECTIVE_BALANCE + deposit_receipt = prepare_deposit_receipt( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_correct_sig_but_forked_state(spec, state): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + # deposits will always be valid, regardless of the current fork + state.fork.current_version = spec.Version('0x1234abcd') + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_incorrect_sig_new_deposit(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount) + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, effective=False) + + +@with_eip6110_and_later +@spec_state_test +def test_top_up__max_effective_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE + state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount + assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE + + +@with_eip6110_and_later +@spec_state_test +def test_top_up__less_effective_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000 + initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + +@with_eip6110_and_later +@spec_state_test +def test_top_up__zero_balance(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, signed=True) + + initial_balance = 0 + initial_effective_balance = 0 + state.balances[validator_index] = initial_balance + state.validators[validator_index].effective_balance = initial_effective_balance + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + assert state.balances[validator_index] == initial_balance + amount + # unchanged effective balance + assert state.validators[validator_index].effective_balance == initial_effective_balance + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_incorrect_sig_top_up(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount) + + # invalid signatures, in top-ups, are allowed! + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_incorrect_withdrawal_credentials_top_up(spec, state): + validator_index = 0 + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:] + deposit_receipt = prepare_deposit_receipt( + spec, + validator_index, + amount, + withdrawal_credentials=withdrawal_credentials + ) + + # inconsistent withdrawal credentials, in top-ups, are allowed! + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_key_validate_invalid_subgroup(spec, state): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + + # All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. + pubkey = b'\x00' * 48 + + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +def test_key_validate_invalid_decompression(spec, state): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + + # `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case. + # This pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception. + pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + pubkey = bytes.fromhex(pubkey_hex) + + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, pubkey=pubkey, signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_ineffective_deposit_with_previous_fork_version(spec, state): + # Since deposits are valid across forks, the domain is always set with `GENESIS_FORK_VERSION`. + # It's an ineffective deposit because it fails at BLS sig verification. + # NOTE: it was effective in Altair. + assert state.fork.previous_version != state.fork.current_version + + yield from run_deposit_receipt_processing_with_specific_fork_version( + spec, + state, + fork_version=state.fork.previous_version, + effective=False, + ) + + +@with_eip6110_and_later +@spec_state_test +@always_bls +def test_effective_deposit_with_genesis_fork_version(spec, state): + assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) + + yield from run_deposit_receipt_processing_with_specific_fork_version( + spec, + state, + fork_version=spec.config.GENESIS_FORK_VERSION, + ) + + +@with_eip6110_and_later +@spec_state_test +def test_success_top_up_to_withdrawn_validator(spec, state): + validator_index = 0 + + # Fully withdraw validator + set_validator_fully_withdrawable(spec, state, validator_index) + assert state.balances[validator_index] > 0 + next_epoch_via_block(spec, state) + assert state.balances[validator_index] == 0 + assert state.validators[validator_index].effective_balance > 0 + next_epoch_via_block(spec, state) + assert state.validators[validator_index].effective_balance == 0 + + # Make a top-up balance to validator + amount = spec.MAX_EFFECTIVE_BALANCE // 4 + deposit_receipt = prepare_deposit_receipt(spec, validator_index, amount, len(state.validators), signed=True) + + yield from run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index) + + assert state.balances[validator_index] == amount + assert state.validators[validator_index].effective_balance == 0 + + validator = state.validators[validator_index] + balance = state.balances[validator_index] + current_epoch = spec.get_current_epoch(state) + assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch) diff --git a/tests/core/pyspec/eth2spec/test/eip6110/sanity/__init__.py b/tests/core/pyspec/eth2spec/test/eip6110/sanity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/__init__.py b/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/__init__.py new file mode 100644 index 000000000..3c0e060f3 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/__init__.py @@ -0,0 +1 @@ +from .test_deposit_transition import * # noqa: F401 F403 diff --git a/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/test_deposit_transition.py b/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/test_deposit_transition.py new file mode 100644 index 000000000..51ef10960 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip6110/sanity/blocks/test_deposit_transition.py @@ -0,0 +1,229 @@ +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, +) +from eth2spec.test.context import ( + spec_state_test, + with_phases, + EIP6110, +) +from eth2spec.test.helpers.deposits import ( + build_deposit_data, + deposit_from_context, + prepare_deposit_receipt, +) +from eth2spec.test.helpers.execution_payload import ( + compute_el_block_hash, +) +from eth2spec.test.helpers.keys import privkeys, pubkeys +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block +) + + +def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True): + """ + Run ``process_block``, yielding: + - pre-state ('pre') + - block ('block') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + yield 'pre', state + + signed_block = state_transition_and_sign_block(spec, state, block, not valid) + + yield 'blocks', [signed_block] + yield 'post', state if valid else None + + # Check that deposits are applied + if valid: + expected_pubkeys = [d.data.pubkey for d in block.body.deposits] + deposit_receipts = block.body.execution_payload.deposit_receipts + expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_receipts if (d.pubkey not in top_up_keys)] + actual_pubkeys = [v.pubkey for v in state.validators[len(state.validators) - len(expected_pubkeys):]] + + assert actual_pubkeys == expected_pubkeys + + +def prepare_state_and_block(spec, + state, + deposit_cnt, + deposit_receipt_cnt, + first_deposit_receipt_index=0, + deposit_receipts_start_index=None, + eth1_data_deposit_count=None): + deposits = [] + deposit_receipts = [] + keypair_index = len(state.validators) + + # Prepare deposits + deposit_data_list = [] + for index in range(deposit_cnt): + deposit_data = build_deposit_data(spec, + pubkeys[keypair_index], + privkeys[keypair_index], + # use max effective balance + spec.MAX_EFFECTIVE_BALANCE, + # insecurely use pubkey as withdrawal key + spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkeys[keypair_index])[1:], + signed=True) + deposit_data_list.append(deposit_data) + keypair_index += 1 + + deposit_root = None + for index in range(deposit_cnt): + deposit, deposit_root, _ = deposit_from_context(spec, deposit_data_list, index) + deposits.append(deposit) + + if deposit_root: + state.eth1_deposit_index = 0 + if not eth1_data_deposit_count: + eth1_data_deposit_count = deposit_cnt + state.eth1_data = spec.Eth1Data(deposit_root=deposit_root, + deposit_count=eth1_data_deposit_count, + block_hash=state.eth1_data.block_hash) + + # Prepare deposit receipts + for offset in range(deposit_receipt_cnt): + deposit_receipt = prepare_deposit_receipt(spec, + keypair_index, + # use max effective balance + spec.MAX_EFFECTIVE_BALANCE, + first_deposit_receipt_index + offset, + signed=True) + deposit_receipts.append(deposit_receipt) + keypair_index += 1 + + # Set start index if defined + if deposit_receipts_start_index: + state.deposit_receipts_start_index = deposit_receipts_start_index + + block = build_empty_block_for_next_slot(spec, state) + + # Assign deposits and deposit receipts + block.body.deposits = deposits + block.body.execution_payload.deposit_receipts = deposit_receipts + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + + return state, block + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__start_index_is_set(spec, state): + # 0 deposits, 2 deposit receipts, unset deposit_receipts_start_index + state, block = prepare_state_and_block(spec, state, + deposit_cnt=0, + deposit_receipt_cnt=2, + first_deposit_receipt_index=state.eth1_data.deposit_count + 11) + + yield from run_deposit_transition_block(spec, state, block) + + # deposit_receipts_start_index must be set to the index of the first receipt + assert state.deposit_receipts_start_index == block.body.execution_payload.deposit_receipts[0].index + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__process_eth1_deposits(spec, state): + # 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index + state, block = prepare_state_and_block(spec, state, + deposit_cnt=3, + deposit_receipt_cnt=1, + first_deposit_receipt_index=11, + deposit_receipts_start_index=7) + + yield from run_deposit_transition_block(spec, state, block) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__process_max_eth1_deposits(spec, state): + # spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index + # state.deposit_receipts_start_index == spec.MAX_DEPOSITS + state, block = prepare_state_and_block(spec, state, + deposit_cnt=spec.MAX_DEPOSITS, + deposit_receipt_cnt=1, + first_deposit_receipt_index=spec.MAX_DEPOSITS + 1, + deposit_receipts_start_index=spec.MAX_DEPOSITS, + eth1_data_deposit_count=23) + + yield from run_deposit_transition_block(spec, state, block) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__process_eth1_deposits_up_to_start_index(spec, state): + # 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count == state.deposit_receipts_start_index + state, block = prepare_state_and_block(spec, state, + deposit_cnt=3, + deposit_receipt_cnt=1, + first_deposit_receipt_index=7, + deposit_receipts_start_index=3) + + yield from run_deposit_transition_block(spec, state, block) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__invalid_not_enough_eth1_deposits(spec, state): + # 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.deposit_receipts_start_index + state, block = prepare_state_and_block(spec, state, + deposit_cnt=3, + deposit_receipt_cnt=1, + first_deposit_receipt_index=29, + deposit_receipts_start_index=23, + eth1_data_deposit_count=17) + + yield from run_deposit_transition_block(spec, state, block, valid=False) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__invalid_too_many_eth1_deposits(spec, state): + # 3 deposits, 1 deposit receipt, state.eth1_data.deposit_count < state.eth1_data_index + state, block = prepare_state_and_block(spec, state, + deposit_cnt=3, + deposit_receipt_cnt=1, + first_deposit_receipt_index=11, + deposit_receipts_start_index=7, + eth1_data_deposit_count=2) + + yield from run_deposit_transition_block(spec, state, block, valid=False) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__invalid_eth1_deposits_overlap_in_protocol_deposits(spec, state): + # spec.MAX_DEPOSITS deposits, 1 deposit receipt, state.eth1_data.deposit_count > state.deposit_receipts_start_index + # state.deposit_receipts_start_index == spec.MAX_DEPOSITS - 1 + state, block = prepare_state_and_block(spec, state, + deposit_cnt=spec.MAX_DEPOSITS, + deposit_receipt_cnt=1, + first_deposit_receipt_index=spec.MAX_DEPOSITS, + deposit_receipts_start_index=spec.MAX_DEPOSITS - 1, + eth1_data_deposit_count=23) + + yield from run_deposit_transition_block(spec, state, block, valid=False) + + +@with_phases([EIP6110]) +@spec_state_test +def test_deposit_transition__deposit_and_top_up_same_block(spec, state): + # 1 deposit, 1 deposit receipt that top ups deposited validator + state, block = prepare_state_and_block(spec, state, + deposit_cnt=1, + deposit_receipt_cnt=1, + first_deposit_receipt_index=11, + deposit_receipts_start_index=7) + + # Artificially assign deposit's pubkey to a deposit receipt of the same block + top_up_keys = [block.body.deposits[0].data.pubkey] + block.body.execution_payload.deposit_receipts[0].pubkey = top_up_keys[0] + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + + yield from run_deposit_transition_block(spec, state, block, top_up_keys=top_up_keys) + + # Check the top up + expected_balance = block.body.deposits[0].data.amount + block.body.execution_payload.deposit_receipts[0].amount + assert state.balances[len(state.balances) - 1] == expected_balance diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 0d31adb43..2140c96e4 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -9,30 +9,31 @@ PHASE0 = SpecForkName('phase0') ALTAIR = SpecForkName('altair') BELLATRIX = SpecForkName('bellatrix') CAPELLA = SpecForkName('capella') +DENEB = SpecForkName('deneb') # Experimental phases (not included in default "ALL_PHASES"): SHARDING = SpecForkName('sharding') CUSTODY_GAME = SpecForkName('custody_game') DAS = SpecForkName('das') -DENEB = SpecForkName('deneb') +EIP6110 = SpecForkName('eip6110') # The forks that pytest can run with. ALL_PHASES = ( # Formal forks - PHASE0, ALTAIR, BELLATRIX, CAPELLA, + PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, # Experimental patches - DENEB, + EIP6110, ) # The forks that output to the test vectors. -TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB) +TESTGEN_FORKS = (PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110) -# TODO: no DENEB fork tests now. Should add when we figure out the content of Capella. ALL_FORK_UPGRADES = { # pre_fork_name: post_fork_name PHASE0: ALTAIR, ALTAIR: BELLATRIX, BELLATRIX: CAPELLA, CAPELLA: DENEB, + DENEB: EIP6110, } ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items() AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0} diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index cfff9c5ef..789b27c8f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -171,6 +171,54 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, return deposit +def build_deposit_receipt(spec, + index, + pubkey, + privkey, + amount, + withdrawal_credentials, + signed): + deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, signed=signed) + return spec.DepositReceipt( + pubkey=deposit_data.pubkey, + withdrawal_credentials=deposit_data.withdrawal_credentials, + amount=deposit_data.amount, + signature=deposit_data.signature, + index=index) + + +def prepare_deposit_receipt(spec, validator_index, amount, + index=None, + pubkey=None, + privkey=None, + withdrawal_credentials=None, + signed=False): + """ + Create a deposit receipt for the given validator, depositing the given amount. + """ + if index is None: + index = validator_index + + if pubkey is None: + pubkey = pubkeys[validator_index] + + if privkey is None: + privkey = privkeys[validator_index] + + # insecurely use pubkey as withdrawal key if no credentials provided + if withdrawal_credentials is None: + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] + + return build_deposit_receipt( + spec, + index, + pubkey, + privkey, + amount, + withdrawal_credentials, + signed, + ) + # # Run processing # @@ -255,3 +303,90 @@ def run_deposit_processing_with_specific_fork_version( state.eth1_data.deposit_count = 1 yield from run_deposit_processing(spec, state, deposit, validator_index, valid=valid, effective=effective) + + +def run_deposit_receipt_processing(spec, state, deposit_receipt, validator_index, valid=True, effective=True): + """ + Run ``process_deposit_receipt``, yielding: + - pre-state ('pre') + - deposit_receipt ('deposit_receipt') + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + pre_validator_count = len(state.validators) + pre_balance = 0 + is_top_up = False + # is a top-up + if validator_index < pre_validator_count: + is_top_up = True + pre_balance = get_balance(state, validator_index) + pre_effective_balance = state.validators[validator_index].effective_balance + + yield 'pre', state + yield 'deposit_receipt', deposit_receipt + + if not valid: + expect_assertion_error(lambda: spec.process_deposit_receipt(state, deposit_receipt)) + yield 'post', None + return + + spec.process_deposit_receipt(state, deposit_receipt) + + yield 'post', state + + if not effective or not bls.KeyValidate(deposit_receipt.pubkey): + assert len(state.validators) == pre_validator_count + assert len(state.balances) == pre_validator_count + if is_top_up: + assert get_balance(state, validator_index) == pre_balance + else: + if is_top_up: + # Top-ups do not change effective balance + assert state.validators[validator_index].effective_balance == pre_effective_balance + assert len(state.validators) == pre_validator_count + assert len(state.balances) == pre_validator_count + else: + # new validator + assert len(state.validators) == pre_validator_count + 1 + assert len(state.balances) == pre_validator_count + 1 + effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit_receipt.amount) + effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT + assert state.validators[validator_index].effective_balance == effective_balance + + assert get_balance(state, validator_index) == pre_balance + deposit_receipt.amount + + +def run_deposit_receipt_processing_with_specific_fork_version( + spec, + state, + fork_version, + valid=True, + effective=True): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + + pubkey = pubkeys[validator_index] + privkey = privkeys[validator_index] + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] + + deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) + domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version) + deposit_data = spec.DepositData( + pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, + signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain)) + ) + deposit_receipt = spec.DepositReceipt( + pubkey=deposit_data.pubkey, + withdrawal_credentials=deposit_data.withdrawal_credentials, + amount=deposit_data.amount, + signature=deposit_data.signature, + index=validator_index) + + yield from run_deposit_receipt_processing( + spec, + state, + deposit_receipt, + validator_index, + valid=valid, + effective=effective + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index c8ef1cbf0..747d678ef 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -4,7 +4,11 @@ from rlp import encode from rlp.sedes import big_endian_int, Binary, List from eth2spec.debug.random_value import get_random_bytes_list -from eth2spec.test.helpers.forks import is_post_capella, is_post_deneb +from eth2spec.test.helpers.forks import ( + is_post_capella, + is_post_deneb, + is_post_eip6110, +) def get_execution_payload_header(spec, execution_payload): @@ -28,6 +32,8 @@ def get_execution_payload_header(spec, execution_payload): payload_header.withdrawals_root = spec.hash_tree_root(execution_payload.withdrawals) if is_post_deneb(spec): payload_header.excess_data_gas = execution_payload.excess_data_gas + if is_post_eip6110(spec): + payload_header.deposit_receipts_root = spec.hash_tree_root(execution_payload.deposit_receipts) return payload_header @@ -48,7 +54,8 @@ def compute_trie_root_from_indexed_data(data): def compute_el_header_block_hash(spec, payload_header, transactions_trie_root, - withdrawals_trie_root=None): + withdrawals_trie_root=None, + deposit_receipts_trie_root=None): """ Computes the RLP execution block hash described by an `ExecutionPayloadHeader`. """ @@ -92,6 +99,10 @@ def compute_el_header_block_hash(spec, if is_post_deneb(spec): # excess_data_gas execution_payload_header_rlp.append((big_endian_int, payload_header.excess_data_gas)) + if is_post_eip6110(spec): + # deposit_receipts_root + assert deposit_receipts_trie_root is not None + execution_payload_header_rlp.append((Binary(32, 32), deposit_receipts_trie_root)) sedes = List([schema for schema, _ in execution_payload_header_rlp]) values = [value for _, value in execution_payload_header_rlp] @@ -118,14 +129,37 @@ def get_withdrawal_rlp(spec, withdrawal): return encode(values, sedes) +def get_deposit_receipt_rlp(spec, deposit_receipt): + deposit_receipt_rlp = [ + # pubkey + (Binary(48, 48), deposit_receipt.pubkey), + # withdrawal_credentials + (Binary(32, 32), deposit_receipt.withdrawal_credentials), + # amount + (big_endian_int, deposit_receipt.amount), + # pubkey + (Binary(96, 96), deposit_receipt.signature), + # index + (big_endian_int, deposit_receipt.index), + ] + + sedes = List([schema for schema, _ in deposit_receipt_rlp]) + values = [value for _, value in deposit_receipt_rlp] + return encode(values, sedes) + + def compute_el_block_hash(spec, payload): transactions_trie_root = compute_trie_root_from_indexed_data(payload.transactions) + withdrawals_trie_root = None + deposit_receipts_trie_root = None + if is_post_capella(spec): withdrawals_encoded = [get_withdrawal_rlp(spec, withdrawal) for withdrawal in payload.withdrawals] withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded) - else: - withdrawals_trie_root = None + if is_post_eip6110(spec): + deposit_receipts_encoded = [get_deposit_receipt_rlp(spec, receipt) for receipt in payload.deposit_receipts] + deposit_receipts_trie_root = compute_trie_root_from_indexed_data(deposit_receipts_encoded) payload_header = get_execution_payload_header(spec, payload) @@ -134,6 +168,7 @@ def compute_el_block_hash(spec, payload): payload_header, transactions_trie_root, withdrawals_trie_root, + deposit_receipts_trie_root, ) @@ -167,6 +202,9 @@ def build_empty_execution_payload(spec, state, randao_mix=None): payload.withdrawals = spec.get_expected_withdrawals(state) if is_post_deneb(spec): payload.excess_data_gas = 0 + if is_post_eip6110(spec): + # just to be clear + payload.deposit_receipts = [] payload.block_hash = compute_el_block_hash(spec, payload) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py index 241c7dc37..68444c472 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py @@ -15,6 +15,7 @@ from eth2spec.test.helpers.constants import ( BELLATRIX, CAPELLA, DENEB, + EIP6110, ) from eth2spec.test.helpers.deposits import ( prepare_state_and_deposit, @@ -158,6 +159,8 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate= state = post_spec.upgrade_to_capella(state) elif post_spec.fork == DENEB: state = post_spec.upgrade_to_deneb(state) + elif post_spec.fork == EIP6110: + state = post_spec.upgrade_to_eip6110(state) assert state.fork.epoch == fork_epoch @@ -173,6 +176,9 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, sync_aggregate= elif post_spec.fork == DENEB: assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION assert state.fork.current_version == post_spec.config.DENEB_FORK_VERSION + elif post_spec.fork == EIP6110: + assert state.fork.previous_version == post_spec.config.DENEB_FORK_VERSION + assert state.fork.current_version == post_spec.config.EIP6110_FORK_VERSION if with_block: return state, _state_transition_and_sign_block_at_slot( diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index be3103e67..5e97522db 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,9 +1,12 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, + EIP6110, ) def is_post_fork(a, b): + if a == EIP6110: + return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110] if a == DENEB: return b in [PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB] if a == CAPELLA: @@ -31,3 +34,7 @@ def is_post_capella(spec): def is_post_deneb(spec): return is_post_fork(spec.fork, DENEB) + + +def is_post_eip6110(spec): + return is_post_fork(spec.fork, EIP6110) diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 0610f11ad..fea259013 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,11 +1,11 @@ from eth2spec.test.helpers.constants import ( - ALTAIR, BELLATRIX, CAPELLA, DENEB, + ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110, ) from eth2spec.test.helpers.execution_payload import ( compute_el_header_block_hash, ) from eth2spec.test.helpers.forks import ( - is_post_altair, is_post_bellatrix, is_post_capella, + is_post_altair, is_post_bellatrix, is_post_capella, is_post_eip6110, ) from eth2spec.test.helpers.keys import pubkeys @@ -47,17 +47,20 @@ def get_sample_genesis_execution_payload_header(spec, ) transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + withdrawals_trie_root = None + deposit_receipts_trie_root = None if is_post_capella(spec): withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - else: - withdrawals_trie_root = None + if is_post_eip6110(spec): + deposit_receipts_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") payload_header.block_hash = compute_el_header_block_hash( spec, payload_header, transactions_trie_root, withdrawals_trie_root, + deposit_receipts_trie_root, ) return payload_header @@ -80,6 +83,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold): elif spec.fork == DENEB: previous_version = spec.config.CAPELLA_FORK_VERSION current_version = spec.config.DENEB_FORK_VERSION + elif spec.fork == EIP6110: + previous_version = spec.config.DENEB_FORK_VERSION + current_version = spec.config.EIP6110_FORK_VERSION state = spec.BeaconState( genesis_time=0, @@ -129,4 +135,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold): eth1_block_hash=eth1_block_hash, ) + if is_post_eip6110(spec): + state.deposit_receipts_start_index = spec.UNSET_DEPOSIT_RECEIPTS_START_INDEX + return state diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py index f5960ff70..30f94b854 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_get_head.py @@ -479,7 +479,7 @@ def test_voting_source_within_two_epoch(spec, state): - store.voting_source[block_root].epoch != store.justified_checkpoint.epoch, and - store.unrealized_justifications[block_root].epoch >= store.justified_checkpoint.epoch, and - store.voting_source[block_root].epoch + 2 >= current_epoch, and - - store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot) + - store.finalized_checkpoint.root == get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch) """ test_steps = [] # Initialization @@ -536,8 +536,11 @@ def test_voting_source_within_two_epoch(spec, state): assert store.unrealized_justifications[last_fork_block_root].epoch >= store.justified_checkpoint.epoch # assert store.voting_source[last_fork_block_root].epoch + 2 >= \ # spec.compute_epoch_at_slot(spec.get_current_slot(store)) - finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - assert store.finalized_checkpoint.root == spec.get_ancestor(store, last_fork_block_root, finalized_slot) + assert store.finalized_checkpoint.root == spec.get_checkpoint_block( + store, + last_fork_block_root, + store.finalized_checkpoint.epoch + ) assert spec.get_head(store) == last_fork_block_root yield 'steps', test_steps @@ -552,7 +555,7 @@ def test_voting_source_beyond_two_epoch(spec, state): - store.voting_source[block_root].epoch != store.justified_checkpoint.epoch, and - store.unrealized_justifications[block_root].epoch >= store.justified_checkpoint.epoch, and - store.voting_source[block_root].epoch + 2 < current_epoch, and - - store.finalized_checkpoint.root == get_ancestor(store, block_root, finalized_slot) + - store.finalized_checkpoint.root == get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch) """ test_steps = [] # Initialization @@ -617,8 +620,11 @@ def test_voting_source_beyond_two_epoch(spec, state): assert store.unrealized_justifications[last_fork_block_root].epoch >= store.justified_checkpoint.epoch # assert store.voting_source[last_fork_block_root].epoch + 2 < \ # spec.compute_epoch_at_slot(spec.get_current_slot(store)) - finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - assert store.finalized_checkpoint.root == spec.get_ancestor(store, last_fork_block_root, finalized_slot) + assert store.finalized_checkpoint.root == spec.get_checkpoint_block( + store, + last_fork_block_root, + store.finalized_checkpoint.epoch + ) assert spec.get_head(store) == correct_head yield 'steps', test_steps @@ -641,7 +647,7 @@ def test_incorrect_finalized(spec, state): # Check that the store doesn't allow for a head block that has: # - store.voting_source[block_root].epoch == store.justified_checkpoint.epoch, and # - store.finalized_checkpoint.epoch != GENESIS_EPOCH, and - # - store.finalized_checkpoint.root != get_ancestor(store, block_root, finalized_slot) + # - store.finalized_checkpoint.root != get_checkpoint_block(store, block_root, store.finalized_checkpoint.epoch) test_steps = [] # Initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) @@ -718,7 +724,11 @@ def test_incorrect_finalized(spec, state): assert store.voting_source[last_fork_block_root].epoch == store.justified_checkpoint.epoch assert store.finalized_checkpoint.epoch != spec.GENESIS_EPOCH finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - assert store.finalized_checkpoint.root != spec.get_ancestor(store, last_fork_block_root, finalized_slot) + assert store.finalized_checkpoint.root != spec.get_checkpoint_block( + store, + block_root, + store.finalized_checkpoint.epoch + ) assert spec.get_head(store) != last_fork_block_root assert spec.get_head(store) == head_root diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py index 0af775339..840413a36 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py @@ -352,8 +352,11 @@ def test_new_finalized_slot_is_not_justified_checkpoint_ancestor(spec, state): # NOTE: Do not call `on_tick` here yield from add_block(spec, store, block, test_steps) - finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = spec.get_ancestor(store, pre_store_justified_checkpoint_root, finalized_slot) + ancestor_at_finalized_slot = spec.get_checkpoint_block( + store, + pre_store_justified_checkpoint_root, + store.finalized_checkpoint.epoch + ) assert ancestor_at_finalized_slot != store.finalized_checkpoint.root assert store.finalized_checkpoint == another_state.finalized_checkpoint @@ -428,8 +431,11 @@ def test_new_finalized_slot_is_justified_checkpoint_ancestor(spec, state): for block in all_blocks: yield from tick_and_add_block(spec, store, block, test_steps) - finalized_slot = spec.compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - ancestor_at_finalized_slot = spec.get_ancestor(store, pre_store_justified_checkpoint_root, finalized_slot) + ancestor_at_finalized_slot = spec.get_checkpoint_block( + store, + pre_store_justified_checkpoint_root, + store.finalized_checkpoint.epoch + ) assert ancestor_at_finalized_slot == store.finalized_checkpoint.root assert store.finalized_checkpoint == another_state.finalized_checkpoint @@ -857,10 +863,18 @@ def test_incompatible_justification_update_start_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - finalized_slot = spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - assert spec.get_ancestor(store, last_block_root, finalized_slot) == state.finalized_checkpoint.root - justified_slot = spec.compute_start_slot_at_epoch(state.current_justified_checkpoint.epoch) - assert spec.get_ancestor(store, last_block_root, justified_slot) != state.current_justified_checkpoint.root + finalized_checkpoint_block = spec.get_checkpoint_block( + store, + last_block_root, + state.finalized_checkpoint.epoch, + ) + assert finalized_checkpoint_block == state.finalized_checkpoint.root + justified_checkpoint_block = spec.get_checkpoint_block( + store, + last_block_root, + state.current_justified_checkpoint.epoch, + ) + assert justified_checkpoint_block != state.current_justified_checkpoint.root assert store.finalized_checkpoint.epoch == 4 assert store.justified_checkpoint.epoch == 6 @@ -934,10 +948,18 @@ def test_incompatible_justification_update_end_of_epoch(spec, state): # Now add the blocks & check that justification update was triggered for signed_block in signed_blocks: yield from tick_and_add_block(spec, store, signed_block, test_steps) - finalized_slot = spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - assert spec.get_ancestor(store, last_block_root, finalized_slot) == state.finalized_checkpoint.root - justified_slot = spec.compute_start_slot_at_epoch(state.current_justified_checkpoint.epoch) - assert spec.get_ancestor(store, last_block_root, justified_slot) != state.current_justified_checkpoint.root + finalized_checkpoint_block = spec.get_checkpoint_block( + store, + last_block_root, + state.finalized_checkpoint.epoch, + ) + assert finalized_checkpoint_block == state.finalized_checkpoint.root + justified_checkpoint_block = spec.get_checkpoint_block( + store, + last_block_root, + state.current_justified_checkpoint.epoch, + ) + assert justified_checkpoint_block != state.current_justified_checkpoint.root assert store.finalized_checkpoint.epoch == 4 assert store.justified_checkpoint.epoch == 6 diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index 810d62578..245ce8565 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -45,6 +45,7 @@ Operations: | `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) | +| `deposit_receipt` | `DepositReceipt` | `deposit_receipt` | `process_deposit_receipt(state, deposit_receipt)` (new in EIP6110) | 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 a485f646a..645c84cb6 100644 --- a/tests/generators/epoch_processing/main.py +++ b/tests/generators/epoch_processing/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -34,6 +34,8 @@ if __name__ == "__main__": deneb_mods = capella_mods + eip6110_mods = deneb_mods + # TODO Custody Game testgen is disabled for now # custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [ # 'reveal_deadlines', @@ -47,6 +49,7 @@ if __name__ == "__main__": BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="epoch_processing", all_mods=all_mods) diff --git a/tests/generators/finality/main.py b/tests/generators/finality/main.py index a25f3b8e7..15c6cad8d 100644 --- a/tests/generators/finality/main.py +++ b/tests/generators/finality/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -8,6 +8,7 @@ if __name__ == "__main__": bellatrix_mods = altair_mods # No additional Bellatrix specific finality tests capella_mods = bellatrix_mods # No additional Capella specific finality tests deneb_mods = capella_mods # No additional Deneb specific finality tests + eip6110_mods = deneb_mods # No additional EIP6110 specific finality tests all_mods = { PHASE0: phase_0_mods, @@ -15,6 +16,7 @@ if __name__ == "__main__": BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="finality", all_mods=all_mods) diff --git a/tests/generators/fork_choice/main.py b/tests/generators/fork_choice/main.py index 4456c2546..b0c9a9bb9 100644 --- a/tests/generators/fork_choice/main.py +++ b/tests/generators/fork_choice/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -19,13 +19,15 @@ if __name__ == "__main__": ]} bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) capella_mods = bellatrix_mods # No additional Capella specific fork choice tests - deneb_mods = capella_mods # No additional Capella specific fork choice tests + deneb_mods = capella_mods # No additional Deneb specific fork choice tests + eip6110_mods = deneb_mods # No additional EIP6110 specific fork choice tests all_mods = { ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="fork_choice", all_mods=all_mods) diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py index e95afcde1..feffde8e3 100644 --- a/tests/generators/genesis/main.py +++ b/tests/generators/genesis/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -17,12 +17,14 @@ if __name__ == "__main__": bellatrix_mods = combine_mods(_new_bellatrix_mods, altair_mods) capella_mods = bellatrix_mods # No additional Capella specific genesis tests deneb_mods = capella_mods # No additional Deneb specific genesis tests + eip6110_mods = deneb_mods # No additional EIP6110 specific genesis tests all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="genesis", all_mods=all_mods) diff --git a/tests/generators/light_client/main.py b/tests/generators/light_client/main.py index cfe34aee4..c6b0e01b9 100644 --- a/tests/generators/light_client/main.py +++ b/tests/generators/light_client/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 from eth2spec.gen_helpers.gen_from_tests.gen import combine_mods, run_state_test_generators @@ -15,12 +15,14 @@ if __name__ == "__main__": ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) deneb_mods = capella_mods + eip6110_mods = deneb_mods all_mods = { ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="light_client", all_mods=all_mods) diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index ed4c6c26c..fc2217917 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -38,6 +38,11 @@ if __name__ == "__main__": deneb_mods = capella_mods + _new_eip6110_mods = {key: 'eth2spec.test.eip6110.block_processing.test_process_' + key for key in [ + 'deposit_receipt', + ]} + eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods) + # TODO Custody Game testgen is disabled for now # _new_custody_game_mods = {key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [ # 'attestation', @@ -54,6 +59,7 @@ if __name__ == "__main__": BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="operations", all_mods=all_mods) diff --git a/tests/generators/rewards/main.py b/tests/generators/rewards/main.py index e6244d172..d01d4a424 100644 --- a/tests/generators/rewards/main.py +++ b/tests/generators/rewards/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -17,6 +17,7 @@ if __name__ == "__main__": bellatrix_mods = altair_mods capella_mods = bellatrix_mods deneb_mods = capella_mods + eip6110_mods = deneb_mods all_mods = { PHASE0: phase_0_mods, @@ -24,6 +25,7 @@ if __name__ == "__main__": BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="rewards", all_mods=all_mods) diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py index 8a6c7b39c..b9f6d7fbb 100644 --- a/tests/generators/sanity/main.py +++ b/tests/generators/sanity/main.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods @@ -28,12 +28,18 @@ if __name__ == "__main__": ]} deneb_mods = combine_mods(_new_deneb_mods, capella_mods) + _new_eip6110_mods = {key: 'eth2spec.test.eip6110.sanity.' + key for key in [ + 'blocks', + ]} + eip6110_mods = combine_mods(_new_eip6110_mods, deneb_mods) + all_mods = { PHASE0: phase_0_mods, ALTAIR: altair_mods, BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="sanity", all_mods=all_mods) diff --git a/tests/generators/sync/main.py b/tests/generators/sync/main.py index 11f05a741..5563e6f8c 100644 --- a/tests/generators/sync/main.py +++ b/tests/generators/sync/main.py @@ -1,5 +1,5 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators -from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, DENEB +from eth2spec.test.helpers.constants import BELLATRIX, CAPELLA, DENEB, EIP6110 if __name__ == "__main__": @@ -8,11 +8,13 @@ if __name__ == "__main__": ]} capella_mods = bellatrix_mods deneb_mods = capella_mods + eip6110_mods = deneb_mods all_mods = { BELLATRIX: bellatrix_mods, CAPELLA: capella_mods, DENEB: deneb_mods, + EIP6110: eip6110_mods, } run_state_test_generators(runner_name="sync", all_mods=all_mods)