Merge pull request #3653 from ralexstokes/init-electra-rebase

Add EIP-7549 to Electra
This commit is contained in:
Hsiao-Wei Wang
2024-04-10 00:13:44 +09:00
committed by GitHub
30 changed files with 279 additions and 494 deletions

View File

@@ -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
@@ -344,7 +328,7 @@ workflows:
- lint:
requires:
- install_pyspec_test
# NOTE: Since phase 0 has been launched, we disabled the deposit contract tests.
# NOTE: Since phase 0 has been launched, we disabled the deposit contract tests.
# - install_deposit_contract_web3_tester:
# requires:
# - checkout_specs

View File

@@ -10,9 +10,9 @@ env:
on:
push:
branches:
- dev
- dev
- master
pull_request:
pull_request:
workflow_dispatch:
inputs:
test_preset_type:
@@ -71,22 +71,22 @@ jobs:
needs: [preclear,lint,codespell,table_of_contents]
strategy:
matrix:
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "eip7549", "whisk", "eip7594"]
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "whisk", "eip7594"]
steps:
- name: Checkout this repo
uses: actions/checkout@v3.2.0
- name: set TEST_PRESET_TYPE
if: github.event.inputs.test_preset_type != ''
if: github.event.inputs.test_preset_type != ''
run: |
echo "spec_test_preset_type=${{ github.event.inputs.test_preset_type || env.TEST_PRESET_TYPE }}" >> $GITHUB_ENV
- name: set TEST_PRESET_TYPE
if: ${{ (github.event_name == 'push' && github.ref_name != 'master') || github.event_name == 'pull_request' }}
run: |
echo "spec_test_preset_type=${{ env.TEST_PRESET_TYPE}}" >> $GITHUB_ENV
echo "spec_test_preset_type=${{ env.TEST_PRESET_TYPE}}" >> $GITHUB_ENV
- name: set TEST_PRESET_TYPE
if: ${{ github.event_name == 'push' && github.ref_name == 'master' }}
run: |
echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV
echo "spec_test_preset_type=mainnet" >> $GITHUB_ENV
- name: set TEST_PRESET_TYPE
if: github.event.schedule=='0 0 * * *'
run: |

1
.gitignore vendored
View File

@@ -22,7 +22,6 @@ tests/core/pyspec/eth2spec/bellatrix/
tests/core/pyspec/eth2spec/capella/
tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/electra/
tests/core/pyspec/eth2spec/eip7549/
tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip7251/
tests/core/pyspec/eth2spec/eip7594/

View File

@@ -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)
@@ -244,5 +244,5 @@ build_docs: copy_docs
mkdocs build
serve_docs:
. venv/bin/activate;
. venv/bin/activate;
mkdocs serve

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -5,10 +5,9 @@ BELLATRIX = 'bellatrix'
CAPELLA = 'capella'
DENEB = 'deneb'
ELECTRA = 'electra'
EIP7251 = 'eip7251'
EIP7549 = 'eip7549'
WHISK = 'whisk'
EIP7594 = 'eip7594'
EIP7251 = 'eip7251'
WHISK = 'whisk'

View File

@@ -7,10 +7,9 @@ from .constants import (
CAPELLA,
DENEB,
ELECTRA,
EIP7251,
EIP7549,
WHISK,
EIP7594,
EIP7251,
)
@@ -21,7 +20,6 @@ PREVIOUS_FORK_OF = {
CAPELLA: BELLATRIX,
DENEB: CAPELLA,
ELECTRA: DENEB,
EIP7549: DENEB,
WHISK: CAPELLA,
EIP7251: DENEB,
EIP7594: DENEB,

View File

@@ -4,7 +4,6 @@ from .bellatrix import BellatrixSpecBuilder
from .capella import CapellaSpecBuilder
from .deneb import DenebSpecBuilder
from .electra import ElectraSpecBuilder
from .eip7549 import EIP7549SpecBuilder
from .whisk import WhiskSpecBuilder
from .eip7251 import EIP7251SpecBuilder
from .eip7594 import EIP7594SpecBuilder
@@ -14,7 +13,6 @@ spec_builders = {
builder.fork: builder
for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
ElectraSpecBuilder, EIP7549SpecBuilder, WhiskSpecBuilder, EIP7251SpecBuilder,
EIP7594SpecBuilder,
ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP7251SpecBuilder,
)
}

View File

@@ -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'''
'''

View File

@@ -1,163 +0,0 @@
# EIP-7549 -- The Beacon Chain
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## 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)
```

View File

@@ -1,141 +0,0 @@
# EIP-7549 -- Fork Logic
**Notice**: This document is a work-in-progress for researchers and implementers.
## Table of contents
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## 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
```

View File

@@ -1,74 +0,0 @@
# Deneb -- Honest Validator
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## 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.

View File

@@ -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.

View File

@@ -20,7 +20,7 @@
## Introduction
This document describes the process of EIP-6110 upgrade.
This document describes the process of the Electra upgrade.
## Configuration

View File

@@ -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
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## 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.
##### Global topics
The `beacon_block` topic is modified to also support Electra blocks.
###### `beacon_aggregate_and_proof`
The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of the new attestation type.
*[Modified in EIP7549]*
The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here.
The derivation of the `message-id` remains stable.
#### 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`

View File

@@ -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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@@ -28,9 +35,40 @@ Please see related Beacon Chain doc before continuing and use them as a referenc
## Block proposal
### Deposits
### Constructing the `BeaconBlockBody`
*[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:
#### 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:
```python
def get_eth1_pending_deposit_count(state: BeaconState) -> uint64:
@@ -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.

View File

@@ -8,7 +8,8 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA,
EIP7251, EIP7549, EIP7594,
EIP7251,
EIP7594,
WHISK,
MINIMAL,
ALL_PHASES,
@@ -520,7 +521,6 @@ 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_electra_and_later = with_all_phases_from(ELECTRA)
with_eip7549_and_later = with_all_phases_from(EIP7549)
with_whisk_and_later = with_all_phases_from(WHISK, all_phases=ALLOWED_TEST_RUNNER_FORKS)
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)

View File

@@ -11,6 +11,7 @@ from eth2spec.test.helpers.block import (
)
from eth2spec.test.helpers.constants import (
AFTER_DENEB_PRE_POST_FORKS,
DENEB,
)
from eth2spec.test.helpers.state import (
next_epoch_via_block,
@@ -78,7 +79,13 @@ def test_transition_attestation_from_previous_fork_with_new_range(
next_epoch_via_block(spec, state)
# Generate an attestation for slot 0 of this epoch
attestation = get_valid_attestation(spec, state, signed=True)
if spec.fork == DENEB:
# NOTE: attestation format changes from Deneb to Electra
# so the attestation must be made with the `post_spec`
target_spec = post_spec
else:
target_spec = spec
attestation = get_valid_attestation(target_spec, state, signed=True)
yield 'pre', state

View File

@@ -5,7 +5,7 @@ from typing import List
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

View File

@@ -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

View File

@@ -17,7 +17,7 @@ ELECTRA = SpecForkName('electra')
SHARDING = SpecForkName('sharding')
CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
EIP7549 = SpecForkName('eip7549')
ELECTRA = SpecForkName('electra')
WHISK = SpecForkName('whisk')
EIP7251 = SpecForkName('eip7251')
EIP7594 = SpecForkName('eip7594')
@@ -39,7 +39,6 @@ ALL_PHASES = (
ELECTRA,
# Experimental patches
EIP7251,
EIP7549,
EIP7594,
)
# The forks that have light client specs
@@ -60,7 +59,6 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB,
# Experimental patches
WHISK: CAPELLA,
EIP7549: DENEB,
EIP7251: DENEB,
EIP7594: DENEB,
}

View File

@@ -17,6 +17,7 @@ from eth2spec.test.helpers.constants import (
PHASE0,
POST_FORK_OF,
PREVIOUS_FORK_OF,
DENEB,
)
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
@@ -296,8 +297,15 @@ def run_transition_with_operation(state,
operation_dict = {'proposer_slashings': [proposer_slashing]}
else:
# operation_type == OperationType.ATTESTER_SLASHING:
if is_at_fork and spec.fork == DENEB:
# NOTE: attestation format changes between Deneb and Electra
# so attester slashing must be made with the `post_spec`
target_spec = post_spec
else:
target_spec = spec
attester_slashing = get_valid_attester_slashing_by_indices(
spec, state,
target_spec, state,
[selected_validator_index],
signed_1=True, signed_2=True,
)

View File

@@ -1,7 +1,6 @@
from .constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
ELECTRA,
EIP7251, EIP7549, WHISK,
ELECTRA, WHISK, EIP7251,
PREVIOUS_FORK_OF,
)
@@ -46,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)

View File

@@ -6,7 +6,7 @@ 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_electra, is_post_whisk, is_post_eip7251
is_post_altair, is_post_bellatrix, is_post_capella, is_post_electra, is_post_whisk, is_post_eip7251,
)
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.whisk import compute_whisk_initial_tracker_cached, compute_whisk_initial_k_commitment_cached

View File

@@ -2,7 +2,7 @@ from eth2spec.test.context import with_all_phases, spec_state_test
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