diff --git a/.circleci/config.yml b/.circleci/config.yml index 013619172d..bf8b7ada8a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -168,19 +168,6 @@ jobs: command: make citest fork=electra - store_test_results: path: tests/core/pyspec/test-reports - test-eip7549: - docker: - - image: circleci/python:3.9 - 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=eip7549 - - store_test_results: - path: tests/core/pyspec/test-reports test-whisk: docker: - image: circleci/python:3.9 @@ -330,9 +317,6 @@ workflows: - test-electra: requires: - install_pyspec_test - - test-eip7549: - requires: - - install_pyspec_test - test-whisk: requires: - install_pyspec_test diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b92fd60dd3..0e9126e392 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -71,7 +71,7 @@ jobs: needs: [preclear,lint,codespell,table_of_contents] strategy: matrix: - version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "whisk", "eip7549", "eip7594"] + version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "whisk", "eip7594"] steps: - name: Checkout this repo uses: actions/checkout@v3.2.0 diff --git a/.gitignore b/.gitignore index acfaae8718..91270239a0 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,6 @@ tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/electra/ tests/core/pyspec/eth2spec/whisk/ tests/core/pyspec/eth2spec/eip7251/ -tests/core/pyspec/eth2spec/eip7549/ tests/core/pyspec/eth2spec/eip7594/ # coverage reports diff --git a/Makefile b/Makefile index 71d07aed79..1025a2a1ea 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SSZ_DIR)/*.md) -ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra eip7549 whisk +ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 7d89833d67..1b0b04a9d6 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -56,9 +56,6 @@ ELECTRA_FORK_EPOCH: 18446744073709551615 # EIP7251 EIP7251_FORK_VERSION: 0x06000000 # temporary stub EIP7251_FORK_EPOCH: 18446744073709551615 -# EIP7549 -EIP7549_FORK_VERSION: 0x06000000 # temporary stub -EIP7549_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x08000000 # temporary stub WHISK_FORK_EPOCH: 18446744073709551615 diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 7e0e444834..b819bd9559 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -55,9 +55,6 @@ ELECTRA_FORK_EPOCH: 18446744073709551615 # EIP7251 EIP7251_FORK_VERSION: 0x06000001 # temporary stub EIP7251_FORK_EPOCH: 18446744073709551615 -# EIP7549 -EIP7549_FORK_VERSION: 0x06000001 # temporary stub -EIP7549_FORK_EPOCH: 18446744073709551615 # WHISK WHISK_FORK_VERSION: 0x08000001 WHISK_FORK_EPOCH: 18446744073709551615 diff --git a/presets/mainnet/eip7549.yaml b/presets/mainnet/eip7549.yaml deleted file mode 100644 index 6d10f82e15..0000000000 --- a/presets/mainnet/eip7549.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Mainnet preset - EIP7594 - -# # Max operations per block -# --------------------------------------------------------------- -# `uint64(2**0)` (= 1) -MAX_ATTESTER_SLASHINGS_EIP7549: 1 -# `uint64(2 * 3)` (= 8) -MAX_ATTESTATIONS_EIP7549: 8 diff --git a/presets/mainnet/electra.yaml b/presets/mainnet/electra.yaml index d38c3e2e3b..e206a65052 100644 --- a/presets/mainnet/electra.yaml +++ b/presets/mainnet/electra.yaml @@ -1,5 +1,12 @@ # Mainnet preset - Electra +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_ELECTRA: 1 +# `uint64(2 * 3)` (= 8) +MAX_ATTESTATIONS_ELECTRA: 8 + # Execution # --------------------------------------------------------------- # 2**13 (= 8192) receipts diff --git a/presets/minimal/eip7549.yaml b/presets/minimal/eip7549.yaml deleted file mode 100644 index 17e21652a3..0000000000 --- a/presets/minimal/eip7549.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Minimal preset - EIP7594 - -# # Max operations per block -# --------------------------------------------------------------- -# `uint64(2**0)` (= 1) -MAX_ATTESTER_SLASHINGS_EIP7549: 1 -# `uint64(2 * 3)` (= 8) -MAX_ATTESTATIONS_EIP7549: 8 diff --git a/presets/minimal/electra.yaml b/presets/minimal/electra.yaml index 5c359728b4..c7750a3cb9 100644 --- a/presets/minimal/electra.yaml +++ b/presets/minimal/electra.yaml @@ -1,5 +1,12 @@ # Minimal preset - Electra +# # Max operations per block +# --------------------------------------------------------------- +# `uint64(2**0)` (= 1) +MAX_ATTESTER_SLASHINGS_ELECTRA: 1 +# `uint64(2 * 3)` (= 8) +MAX_ATTESTATIONS_ELECTRA: 8 + # Execution # --------------------------------------------------------------- # [customized] diff --git a/pysetup/constants.py b/pysetup/constants.py index eb272d60ae..299f96132a 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -7,7 +7,6 @@ ELECTRA = 'electra' EIP7594 = 'eip7594' EIP7251 = 'eip7251' -EIP7549 = 'eip7549' WHISK = 'whisk' diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index 8c15fc719b..c7c9ebf047 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -10,7 +10,6 @@ WHISK, EIP7594, EIP7251, - EIP7549, ) @@ -23,7 +22,6 @@ ELECTRA: DENEB, WHISK: CAPELLA, EIP7251: DENEB, - EIP7549: DENEB, EIP7594: DENEB, } diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index cf8a49972d..63519f57f3 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -6,7 +6,6 @@ from .electra import ElectraSpecBuilder from .whisk import WhiskSpecBuilder from .eip7251 import EIP7251SpecBuilder -from .eip7549 import EIP7549SpecBuilder from .eip7594 import EIP7594SpecBuilder @@ -14,6 +13,6 @@ builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP7549SpecBuilder, EIP7251SpecBuilder, + ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP7251SpecBuilder, ) } diff --git a/pysetup/spec_builders/eip7549.py b/pysetup/spec_builders/eip7549.py deleted file mode 100644 index bd5dbbeaec..0000000000 --- a/pysetup/spec_builders/eip7549.py +++ /dev/null @@ -1,11 +0,0 @@ -from .base import BaseSpecBuilder -from ..constants import EIP7549 - - -class EIP7549SpecBuilder(BaseSpecBuilder): - fork: str = EIP7549 - - @classmethod - def imports(cls, preset_name: str): - return super().imports(preset_name) + f''' -''' diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md deleted file mode 100644 index 9ee88e7def..0000000000 --- a/specs/_features/eip7549/beacon-chain.md +++ /dev/null @@ -1,163 +0,0 @@ -# EIP-7549 -- The Beacon Chain - -## Table of contents - - - - - -- [Introduction](#introduction) -- [Preset](#preset) -- [Containers](#containers) - - [Modified containers](#modified-containers) - - [`Attestation`](#attestation) - - [`IndexedAttestation`](#indexedattestation) - - [`BeaconBlockBody`](#beaconblockbody) -- [Helper functions](#helper-functions) - - [Misc](#misc) - - [`get_committee_indices`](#get_committee_indices) - - [Beacon state accessors](#beacon-state-accessors) - - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - - [Block processing](#block-processing) - - [Modified `process_attestation`](#modified-process_attestation) - - - - -## Introduction - -This is the beacon chain specification to move the attestation committee index outside of the signed message. For motivation, refer to [EIP-7549](https://eips.ethereum.org/EIPS/eip-7549). - -*Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. - -## Preset - -| Name | Value | Description | -| - | - | - | -| `MAX_ATTESTER_SLASHINGS_EIP7549` | `2**0` (= 1) | -| `MAX_ATTESTATIONS_EIP7549` | `2**3` (= 8) | - -## Containers - -### Modified containers - -#### `Attestation` - -```python -class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] - data: AttestationData - committee_bits: Bitvector[MAX_COMMITTEES_PER_SLOT] # [New in EIP7549] - signature: BLSSignature -``` - -#### `IndexedAttestation` - -```python -class IndexedAttestation(Container): - # [Modified in EIP7549] - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] - data: AttestationData - signature: BLSSignature -``` - -#### `BeaconBlockBody` - -```python -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_EIP7549] # [Modified in EIP7549] - attestations: List[Attestation, MAX_ATTESTATIONS_EIP7549] # [Modified in EIP7549] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - # Execution - execution_payload: ExecutionPayload - bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] - blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] -``` - -## Helper functions - -### Misc - -#### `get_committee_indices` - -```python -def get_committee_indices(commitee_bits: Bitvector) -> Sequence[CommitteeIndex]: - return [CommitteeIndex(index) for index, bit in enumerate(commitee_bits) if bit] -``` - -### Beacon state accessors - -#### Modified `get_attesting_indices` - -```python -def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: - """ - Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``. - """ - output: Set[ValidatorIndex] = set() - committee_indices = get_committee_indices(attestation.committee_bits) - committee_offset = 0 - for index in committee_indices: - committee = get_beacon_committee(state, attestation.data.slot, index) - committee_attesters = set( - index for i, index in enumerate(committee) if attestation.aggregation_bits[committee_offset + i]) - output = output.union(committee_attesters) - - committee_offset += len(committee) - - return output -``` - -### Block processing - -#### Modified `process_attestation` - -```python -def process_attestation(state: BeaconState, attestation: Attestation) -> None: - data = attestation.data - assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) - assert data.target.epoch == compute_epoch_at_slot(data.slot) - assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot - - # [Modified in EIP7549] - assert data.index == 0 - committee_indices = get_committee_indices(attestation.committee_bits) - participants_count = 0 - for index in committee_indices: - assert index < get_committee_count_per_slot(state, data.target.epoch) - committee = get_beacon_committee(state, data.slot, index) - participants_count += len(committee) - - assert len(attestation.aggregation_bits) == participants_count - - # Participation flag indices - participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot) - - # Verify signature - assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) - - # Update epoch participation flags - if data.target.epoch == get_current_epoch(state): - epoch_participation = state.current_epoch_participation - else: - epoch_participation = state.previous_epoch_participation - - proposer_reward_numerator = 0 - for index in get_attesting_indices(state, attestation): - for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS): - if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): - epoch_participation[index] = add_flag(epoch_participation[index], flag_index) - proposer_reward_numerator += get_base_reward(state, index) * weight - - # Reward proposer - proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT - proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator) - increase_balance(state, get_beacon_proposer_index(state), proposer_reward) -``` diff --git a/specs/_features/eip7549/fork.md b/specs/_features/eip7549/fork.md deleted file mode 100644 index 9294670db8..0000000000 --- a/specs/_features/eip7549/fork.md +++ /dev/null @@ -1,141 +0,0 @@ -# EIP-7549 -- Fork Logic - -**Notice**: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - - -- [Introduction](#introduction) -- [Configuration](#configuration) -- [Helper functions](#helper-functions) - - [Misc](#misc) - - [Modified `compute_fork_version`](#modified-compute_fork_version) -- [Fork to EIP-7549](#fork-to-eip-7549) - - [Fork trigger](#fork-trigger) - - [Upgrading the state](#upgrading-the-state) - - - -## Introduction - -This document describes the process of EIP-7549 upgrade. - -## Configuration - -Warning: this configuration is not definitive. - -| Name | Value | -| - | - | -| `EIP7549_FORK_VERSION` | `Version('0x06000000')` | -| `EIP7549_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | - -## Helper functions - -### Misc - -#### Modified `compute_fork_version` - -```python -def compute_fork_version(epoch: Epoch) -> Version: - """ - Return the fork version at the given ``epoch``. - """ - if epoch >= EIP7549_FORK_EPOCH: - return EIP7549_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: - return BELLATRIX_FORK_VERSION - if epoch >= ALTAIR_FORK_EPOCH: - return ALTAIR_FORK_VERSION - return GENESIS_FORK_VERSION -``` - -## Fork to EIP-7549 - -### Fork trigger - -TBD. This fork is defined for testing purposes, the EIP may be combined with other consensus-layer upgrade. -For now, we assume the condition will be triggered at epoch `EIP7549_FORK_EPOCH`. - -Note that for the pure EIP-7549 networks, we don't apply `upgrade_to_eip7549` since it starts with EIP-7549 version logic. - -### Upgrading the state - -If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP7549_FORK_EPOCH`, -an irregular state change is made to upgrade to EIP-7549. - -```python -def upgrade_to_eip7549(pre: capella.BeaconState) -> BeaconState: - epoch = capella.get_current_epoch(pre) - latest_execution_payload_header = ExecutionPayloadHeader( - parent_hash=pre.latest_execution_payload_header.parent_hash, - fee_recipient=pre.latest_execution_payload_header.fee_recipient, - state_root=pre.latest_execution_payload_header.state_root, - receipts_root=pre.latest_execution_payload_header.receipts_root, - logs_bloom=pre.latest_execution_payload_header.logs_bloom, - prev_randao=pre.latest_execution_payload_header.prev_randao, - block_number=pre.latest_execution_payload_header.block_number, - gas_limit=pre.latest_execution_payload_header.gas_limit, - gas_used=pre.latest_execution_payload_header.gas_used, - timestamp=pre.latest_execution_payload_header.timestamp, - extra_data=pre.latest_execution_payload_header.extra_data, - base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas, - block_hash=pre.latest_execution_payload_header.block_hash, - transactions_root=pre.latest_execution_payload_header.transactions_root, - withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, - ) - post = BeaconState( - # Versioning - genesis_time=pre.genesis_time, - genesis_validators_root=pre.genesis_validators_root, - slot=pre.slot, - fork=Fork( - previous_version=pre.fork.current_version, - current_version=EIP7549_FORK_VERSION, # [Modified in EIP-7549] - epoch=epoch, - ), - # History - latest_block_header=pre.latest_block_header, - block_roots=pre.block_roots, - state_roots=pre.state_roots, - historical_roots=pre.historical_roots, - # Eth1 - eth1_data=pre.eth1_data, - eth1_data_votes=pre.eth1_data_votes, - eth1_deposit_index=pre.eth1_deposit_index, - # Registry - validators=pre.validators, - balances=pre.balances, - # Randomness - randao_mixes=pre.randao_mixes, - # Slashings - slashings=pre.slashings, - # Participation - previous_epoch_participation=pre.previous_epoch_participation, - current_epoch_participation=pre.current_epoch_participation, - # Finality - justification_bits=pre.justification_bits, - previous_justified_checkpoint=pre.previous_justified_checkpoint, - current_justified_checkpoint=pre.current_justified_checkpoint, - finalized_checkpoint=pre.finalized_checkpoint, - # Inactivity - inactivity_scores=pre.inactivity_scores, - # Sync - current_sync_committee=pre.current_sync_committee, - next_sync_committee=pre.next_sync_committee, - # Execution-layer - latest_execution_payload_header=latest_execution_payload_header, - # Withdrawals - next_withdrawal_index=pre.next_withdrawal_index, - next_withdrawal_validator_index=pre.next_withdrawal_validator_index, - # Deep history valid from Capella onwards - historical_summaries=pre.historical_summaries, - ) - - return post -``` diff --git a/specs/_features/eip7549/validator.md b/specs/_features/eip7549/validator.md deleted file mode 100644 index d904e23f92..0000000000 --- a/specs/_features/eip7549/validator.md +++ /dev/null @@ -1,74 +0,0 @@ -# Deneb -- Honest Validator - -## Table of contents - - - - - -- [Modifications in EIP-7549](#modifications-in-eip-7549) - - [Block proposal](#block-proposal) - - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [Attester slashings](#attester-slashings) - - [Attestations](#attestations) - - [Attesting](#attesting) - - [Construct attestation](#construct-attestation) - - [Attestation aggregation](#attestation-aggregation) - - [Construct aggregate](#construct-aggregate) - - - - -## Modifications in EIP-7549 - -### Block proposal - -#### Constructing the `BeaconBlockBody` - -##### Attester slashings - -Changed the max attestations size to `MAX_ATTESTER_SLASHINGS_EIP7549`. - -##### Attestations - -The network attestation aggregates contain only the assigned committee attestations. -Attestation aggregates received by the block proposer from the committee aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. -The proposer should run the following function to construct an on chain final aggregate form a list of network aggregates with equal `AttestationData`: - -```python -def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Attestation: - aggregates = sorted(network_aggregates, key=lambda a: get_committee_indices(a.committee_bits)[0]) - - data = aggregates[0].data - aggregation_bits = Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]() - for a in aggregates: - for b in a.aggregation_bits: - aggregation_bits.append(b) - - signature = bls.Aggregate([a.signature for a in aggregates]) - - committee_indices = [get_committee_indices(a.committee_bits)[0] for a in aggregates] - committee_flags = [(index in committee_indices) for index in range(0, MAX_COMMITTEES_PER_SLOT)] - committee_bits = Bitvector[MAX_COMMITTEES_PER_SLOT](committee_flags) - - return Attestation(aggregation_bits, data, committee_bits, signature) -``` - -### Attesting - -#### Construct attestation - -- Set `attestation_data.index = 0`. -- Let `attestation.aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. -- Let `attestation.committee_bits` be a `Bitvector[MAX_COMMITTEES_PER_SLOT]`, where the bit at the index associated with the validator's committee is set to `0b1`. - -*Note*: Calling `get_attesting_indices(state, attestation)` should return a list of length equal to 1, containing `validator_index`. - -### Attestation aggregation - -#### Construct aggregate - -- Set `attestation_data.index = 0`. -- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. -- Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the same value as in each individual attestation. - diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 9c9066c3e2..ea33ff5341 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -18,12 +18,21 @@ - [`DepositReceipt`](#depositreceipt) - [`ExecutionLayerExit`](#executionlayerexit) - [Extended Containers](#extended-containers) + - [`Attestation`](#attestation) + - [`IndexedAttestation`](#indexedattestation) + - [`BeaconBlockBody`](#beaconblockbody) - [`ExecutionPayload`](#executionpayload) - [`ExecutionPayloadHeader`](#executionpayloadheader) - [`BeaconState`](#beaconstate) +- [Helper functions](#helper-functions) + - [Misc](#misc-1) + - [`get_committee_indices`](#get_committee_indices) + - [Beacon state accessors](#beacon-state-accessors) + - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_operations`](#modified-process_operations) + - [Modified `process_attestation`](#modified-process_attestation) - [New `process_deposit_receipt`](#new-process_deposit_receipt) - [New `process_execution_layer_exit`](#new-process_execution_layer_exit) - [Modified `process_execution_payload`](#modified-process_execution_payload) @@ -37,6 +46,9 @@ Electra is a consensus-layer upgrade containing a number of features. Including: * [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110): Supply validator deposits on chain * [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002): Execution layer triggerable exits +* [EIP-7549](https://eips.ethereum.org/EIPS/eip-7549): Move committee index outside Attestation + +*Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. ## Constants @@ -56,6 +68,8 @@ The following values are (non-configurable) constants used throughout the specif | - | - | - | | `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload | | `MAX_EXECUTION_LAYER_EXITS` | `2**4` (= 16) | *[New in Electra:EIP7002]* | +| `MAX_ATTESTER_SLASHINGS_ELECTRA` | `2**0` (= 1) | *[New in Electra:EIP7549]* | +| `MAX_ATTESTATIONS_ELECTRA` | `2**3` (= 8) | *[New in Electra:EIP7549]* | ## Containers @@ -86,6 +100,46 @@ class ExecutionLayerExit(Container): ### Extended Containers +#### `Attestation` + +```python +class Attestation(Container): + aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] # [Modified in Electra:EIP7549] + data: AttestationData + committee_bits: Bitvector[MAX_COMMITTEES_PER_SLOT] # [New in Electra:EIP7549] + signature: BLSSignature +``` + +#### `IndexedAttestation` + +```python +class IndexedAttestation(Container): + # [Modified in Electra:EIP7549] + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] + data: AttestationData + signature: BLSSignature +``` + +#### `BeaconBlockBody` + +```python +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] + attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA] # [Modified in Electra:EIP7549] + attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA] # [Modified in Electra:EIP7549] + deposits: List[Deposit, MAX_DEPOSITS] + voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] + sync_aggregate: SyncAggregate + # Execution + execution_payload: ExecutionPayload # [Modified in Electra:EIP6110:EIP7002] + bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] +``` + #### `ExecutionPayload` ```python @@ -189,6 +243,40 @@ class BeaconState(Container): deposit_receipts_start_index: uint64 ``` +## Helper functions + +### Misc + +#### `get_committee_indices` + +```python +def get_committee_indices(commitee_bits: Bitvector) -> Sequence[CommitteeIndex]: + return [CommitteeIndex(index) for index, bit in enumerate(commitee_bits) if bit] +``` + +### Beacon state accessors + +#### Modified `get_attesting_indices` + +```python +def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: + """ + Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``. + """ + output: Set[ValidatorIndex] = set() + committee_indices = get_committee_indices(attestation.committee_bits) + committee_offset = 0 + for index in committee_indices: + committee = get_beacon_committee(state, attestation.data.slot, index) + committee_attesters = set( + index for i, index in enumerate(committee) if attestation.aggregation_bits[committee_offset + i]) + output = output.union(committee_attesters) + + committee_offset += len(committee) + + return output +``` + ## Beacon chain state transition function ### Block processing @@ -200,13 +288,13 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in Electra:EIP6110] process_randao(state, block.body) process_eth1_data(state, block.body) - process_operations(state, block.body) # [Modified in Electra:EIP6110:EIP7002] + process_operations(state, block.body) # [Modified in Electra:EIP6110:EIP7002:EIP7549] process_sync_aggregate(state, block.body.sync_aggregate) ``` #### Modified `process_operations` -*Note*: The function `process_operations` is modified to process `DepositReceipt` and `ExecutionLayerExit` operations included in the payload. +*Note*: The function `process_operations` is modified to process `DepositReceipt` and `ExecutionLayerExit` operations included in the payload, along with the new attestation format. ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: @@ -224,7 +312,7 @@ 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.attestations, process_attestation) # [Modified in Electra:EIP7549] for_ops(body.deposits, process_deposit) for_ops(body.voluntary_exits, process_voluntary_exit) for_ops(body.execution_payload.exits, process_execution_layer_exit) # [New in Electra:EIP7002] @@ -234,6 +322,51 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) ``` +#### Modified `process_attestation` + +```python +def process_attestation(state: BeaconState, attestation: Attestation) -> None: + data = attestation.data + assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) + assert data.target.epoch == compute_epoch_at_slot(data.slot) + assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot + + # [Modified in Electra:EIP7549] + assert data.index == 0 + committee_indices = get_committee_indices(attestation.committee_bits) + participants_count = 0 + for index in committee_indices: + assert index < get_committee_count_per_slot(state, data.target.epoch) + committee = get_beacon_committee(state, data.slot, index) + participants_count += len(committee) + + assert len(attestation.aggregation_bits) == participants_count + + # Participation flag indices + participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot) + + # Verify signature + assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) + + # Update epoch participation flags + if data.target.epoch == get_current_epoch(state): + epoch_participation = state.current_epoch_participation + else: + epoch_participation = state.previous_epoch_participation + + proposer_reward_numerator = 0 + for index in get_attesting_indices(state, attestation): + for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS): + if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): + epoch_participation[index] = add_flag(epoch_participation[index], flag_index) + proposer_reward_numerator += get_base_reward(state, index) * weight + + # Reward proposer + proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT + proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator) + increase_balance(state, get_beacon_proposer_index(state), proposer_reward) +``` + #### New `process_deposit_receipt` *Note*: This function is new in Electra:EIP6110. diff --git a/specs/_features/eip7549/p2p-interface.md b/specs/electra/p2p-interface.md similarity index 60% rename from specs/_features/eip7549/p2p-interface.md rename to specs/electra/p2p-interface.md index 20aa291b3f..ebdcaaa831 100644 --- a/specs/_features/eip7549/p2p-interface.md +++ b/specs/electra/p2p-interface.md @@ -1,6 +1,6 @@ -# EIP-7549 -- Networking +# Electra -- Networking -This document contains the consensus-layer networking specification for EIP-7549. +This document contains the consensus-layer networking specification for Electra. The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. @@ -10,29 +10,37 @@ The specification of these changes continues in the same format as the network s -- [Modifications in EIP-7549](#modifications-in-eip-7549) +- [Modifications in Electra](#modifications-in-electra) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - - [Global topics](#global-topics) - - [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof) - - [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id) + - [Global topics](#global-topics) + - [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof) + - [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id) -## Modifications in EIP-7549 +## Modifications in Electra ### The gossip domain: gossipsub +Some gossip meshes are upgraded in the fork of Electra to support upgraded types. + #### Topics and messages -The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of a new attestation type. +Topics follow the same specification as in prior upgrades. + +The `beacon_block` topic is modified to also support Electra blocks. + +The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of the new attestation type. -##### Global topics +The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here. -###### `beacon_aggregate_and_proof` +The derivation of the `message-id` remains stable. -*[Modified in EIP7549]* +#### Global topics + +##### `beacon_aggregate_and_proof` The following convenience variables are re-defined - `index = get_committee_indices(aggregate.committee_bits)[0]` @@ -41,7 +49,7 @@ The following validations are added: * [REJECT] `len(committee_indices) == 1`, where `committee_indices = get_committee_indices(aggregate)`. * [REJECT] `aggregate.data.index == 0` -###### `beacon_attestation_{subnet_id}` +##### `beacon_attestation_{subnet_id}` The following convenience variables are re-defined - `index = get_committee_indices(attestation.committee_bits)[0]` @@ -49,4 +57,3 @@ The following convenience variables are re-defined The following validations are added: * [REJECT] `len(committee_indices) == 1`, where `committee_indices = get_committee_indices(attestation)`. * [REJECT] `attestation.data.index == 0` - diff --git a/specs/electra/validator.md b/specs/electra/validator.md index 623041f524..cc118bfb66 100644 --- a/specs/electra/validator.md +++ b/specs/electra/validator.md @@ -9,7 +9,14 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) - [Block proposal](#block-proposal) - - [Deposits](#deposits) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [Attester slashings](#attester-slashings) + - [Attestations](#attestations) + - [Deposits](#deposits) +- [Attesting](#attesting) + - [Construct attestation](#construct-attestation) +- [Attestation aggregation](#attestation-aggregation) + - [Construct aggregate](#construct-aggregate) @@ -28,7 +35,38 @@ Please see related Beacon Chain doc before continuing and use them as a referenc ## Block proposal -### Deposits +### Constructing the `BeaconBlockBody` + +#### Attester slashings + +Changed the max attestations size to `MAX_ATTESTER_SLASHINGS_ELECTRA`. + +#### Attestations + +The network attestation aggregates contain only the assigned committee attestations. +Attestation aggregates received by the block proposer from the committee aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. +The proposer should run the following function to construct an on chain final aggregate form a list of network aggregates with equal `AttestationData`: + +```python +def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Attestation: + aggregates = sorted(network_aggregates, key=lambda a: get_committee_indices(a.committee_bits)[0]) + + data = aggregates[0].data + aggregation_bits = Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]() + for a in aggregates: + for b in a.aggregation_bits: + aggregation_bits.append(b) + + signature = bls.Aggregate([a.signature for a in aggregates]) + + committee_indices = [get_committee_indices(a.committee_bits)[0] for a in aggregates] + committee_flags = [(index in committee_indices) for index in range(0, MAX_COMMITTEES_PER_SLOT)] + committee_bits = Bitvector[MAX_COMMITTEES_PER_SLOT](committee_flags) + + return Attestation(aggregation_bits, data, committee_bits, signature) +``` + +#### Deposits *[New in Electra:EIP6110]* The expected number of deposits MUST be changed from `min(MAX_DEPOSITS, eth1_data.deposit_count - state.eth1_deposit_index)` to the result of the following function: @@ -40,3 +78,21 @@ def get_eth1_pending_deposit_count(state: BeaconState) -> uint64: else: return uint64(0) ``` + +## Attesting + +### Construct attestation + +- Set `attestation_data.index = 0`. +- Let `attestation.aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. +- Let `attestation.committee_bits` be a `Bitvector[MAX_COMMITTEES_PER_SLOT]`, where the bit at the index associated with the validator's committee is set to `0b1`. + +*Note*: Calling `get_attesting_indices(state, attestation)` should return a list of length equal to 1, containing `validator_index`. + +## Attestation aggregation + +### Construct aggregate + +- Set `attestation_data.index = 0`. +- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. +- Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the same value as in each individual attestation. diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index e8568df20e..064bdea5c8 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -9,7 +9,6 @@ from .helpers.constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA, EIP7251, - EIP7549, EIP7594, WHISK, MINIMAL, @@ -523,7 +522,6 @@ def wrapper(*args, spec: Spec, **kw): with_deneb_and_later = with_all_phases_from(DENEB) with_electra_and_later = with_all_phases_from(ELECTRA) with_whisk_and_later = with_all_phases_from(WHISK, all_phases=ALLOWED_TEST_RUNNER_FORKS) -with_eip7549_and_later = with_all_phases_from(EIP7549) with_eip7594_and_later = with_all_phases_from(EIP7594, all_phases=ALLOWED_TEST_RUNNER_FORKS) with_eip7251_and_later = with_all_phases_from(EIP7251, all_phases=ALLOWED_TEST_RUNNER_FORKS) diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 916a7ca00d..68dd884709 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -5,7 +5,7 @@ from eth2spec.test.context import expect_assertion_error from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.block import build_empty_block_for_next_slot -from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb, is_post_eip7549 +from eth2spec.test.helpers.forks import is_post_altair, is_post_deneb, is_post_electra from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls from eth2spec.utils.ssz.ssz_typing import Bitlist @@ -78,7 +78,7 @@ def build_attestation_data(spec, state, slot, index, beacon_block_root=None, sha data = spec.AttestationData( slot=slot, - index=0 if is_post_eip7549(spec) else index, + index=0 if is_post_electra(spec) else index, beacon_block_root=beacon_block_root, source=spec.Checkpoint(epoch=source_epoch, root=source_root), target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root), @@ -174,7 +174,7 @@ def fill_aggregate_attestation(spec, state, attestation, committee_index, signed participants = filter_participant_set(participants) # initialize `aggregation_bits` - if is_post_eip7549(spec): + if is_post_electra(spec): attestation.committee_bits[committee_index] = True attestation.aggregation_bits = get_empty_eip7549_aggregation_bits( spec, state, attestation.committee_bits, attestation.data.slot) @@ -184,7 +184,7 @@ def fill_aggregate_attestation(spec, state, attestation, committee_index, signed # fill in the `aggregation_bits` for i in range(len(beacon_committee)): - if is_post_eip7549(spec): + if is_post_electra(spec): offset = get_eip7549_aggregation_bits_offset( spec, state, attestation.data.slot, attestation.committee_bits, committee_index) aggregation_bits_index = offset + i @@ -272,10 +272,10 @@ def _aggregate_aggregation_bits_and_signatures(spec, state, slot, aggregate, att def get_valid_attestation_at_slot(state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None): """ - Return the aggregate attestation post EIP-7549. + Return the aggregate attestation post Electra. Note: this EIP supports dense packing of on-chain aggregates so we can just return a single `Attestation`. """ - assert is_post_eip7549(spec) + assert is_post_electra(spec) attestations = list(get_valid_attestations_at_slot( state, spec, slot_to_attest, participation_fn=participation_fn, @@ -322,7 +322,7 @@ def next_slots_with_attestations(spec, def _add_valid_attestations(spec, state, block, slot_to_attest, participation_fn=None): - if is_post_eip7549(spec): + if is_post_electra(spec): attestation = get_valid_attestation_at_slot( state, spec, @@ -482,8 +482,8 @@ def cached_prepare_state_with_attestations(spec, state): def get_max_attestations(spec): - if is_post_eip7549(spec): - return spec.MAX_ATTESTATIONS_EIP7549 + if is_post_electra(spec): + return spec.MAX_ATTESTATIONS_ELECTRA else: return spec.MAX_ATTESTATIONS diff --git a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py index bdda3bf766..c16096565e 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py @@ -1,5 +1,5 @@ from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation, sign_indexed_attestation -from eth2spec.test.helpers.forks import is_post_eip7549 +from eth2spec.test.helpers.forks import is_post_electra def get_valid_attester_slashing(spec, state, slot=None, signed_1=False, signed_2=False, filter_participant_set=None): @@ -66,7 +66,7 @@ def get_attestation_2_data(spec, att_slashing): def get_max_attester_slashings(spec): - if is_post_eip7549(spec): - return spec.MAX_ATTESTER_SLASHINGS_EIP7549 + if is_post_electra(spec): + return spec.MAX_ATTESTER_SLASHINGS_ELECTRA else: return spec.MAX_ATTESTER_SLASHINGS diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index f8e555afbd..d5ba2f5c1b 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -19,7 +19,6 @@ ELECTRA = SpecForkName('electra') WHISK = SpecForkName('whisk') EIP7251 = SpecForkName('eip7251') -EIP7549 = SpecForkName('eip7549') EIP7594 = SpecForkName('eip7594') # @@ -39,7 +38,6 @@ ELECTRA, # Experimental patches EIP7251, - EIP7549, EIP7594, ) # The forks that have light client specs @@ -60,7 +58,6 @@ ELECTRA: DENEB, # Experimental patches WHISK: CAPELLA, - EIP7549: DENEB, EIP7251: DENEB, EIP7594: DENEB, } diff --git a/tests/core/pyspec/eth2spec/test/helpers/forks.py b/tests/core/pyspec/eth2spec/test/helpers/forks.py index 238a39b8ca..c288b3e6ed 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/forks.py +++ b/tests/core/pyspec/eth2spec/test/helpers/forks.py @@ -1,6 +1,6 @@ from .constants import ( PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, - ELECTRA, WHISK, EIP7251, EIP7549, + ELECTRA, WHISK, EIP7251, PREVIOUS_FORK_OF, ) @@ -45,10 +45,6 @@ def is_post_eip7251(spec): return is_post_fork(spec.fork, EIP7251) -def is_post_eip7549(spec): - return is_post_fork(spec.fork, EIP7549) - - def is_post_whisk(spec): return is_post_fork(spec.fork, WHISK) diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py index 128c6bd704..e3ec931b32 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/fork_choice/test_on_attestation.py @@ -2,7 +2,7 @@ from eth2spec.test.helpers.block import build_empty_block_for_next_slot from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation from eth2spec.test.helpers.constants import ALL_PHASES -from eth2spec.test.helpers.forks import is_post_eip7549 +from eth2spec.test.helpers.forks import is_post_electra from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store @@ -326,7 +326,7 @@ def test_on_attestation_invalid_attestation(spec, state): attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True) # make invalid by using an invalid committee index - if is_post_eip7549(spec): + if is_post_electra(spec): attestation.committee_bits = spec.Bitvector[spec.MAX_COMMITTEES_PER_SLOT]() else: attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH