Merge EIP-7549 into Electra

This commit is contained in:
Alex Stokes 2024-04-05 11:02:03 -06:00
parent 4287340401
commit e4068b5391
No known key found for this signature in database
26 changed files with 247 additions and 478 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

View File

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

1
.gitignore vendored
View File

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

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)

View File

@ -51,14 +51,11 @@ CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC
DENEB_FORK_VERSION: 0x04000000
DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC
# Electra
ELECTRA_FORK_VERSION: 0x05000000 # temporary stub
ELECTRA_FORK_VERSION: 0x05000000
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

@ -7,7 +7,6 @@ DENEB = 'deneb'
ELECTRA = 'electra'
EIP7594 = 'eip7594'
EIP7251 = 'eip7251'
EIP7549 = 'eip7549'
WHISK = 'whisk'

View File

@ -10,7 +10,6 @@ from .constants import (
WHISK,
EIP7594,
EIP7251,
EIP7549,
)
@ -23,7 +22,6 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB,
WHISK: CAPELLA,
EIP7251: DENEB,
EIP7549: DENEB,
EIP7594: DENEB,
}

View File

@ -6,7 +6,6 @@ from .deneb import DenebSpecBuilder
from .electra import ElectraSpecBuilder
from .whisk import WhiskSpecBuilder
from .eip7251 import EIP7251SpecBuilder
from .eip7549 import EIP7549SpecBuilder
from .eip7594 import EIP7594SpecBuilder
@ -14,6 +13,6 @@ spec_builders = {
builder.fork: builder
for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP7549SpecBuilder, EIP7251SpecBuilder,
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

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

View File

@ -9,7 +9,6 @@ from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA,
EIP7251,
EIP7549,
EIP7594,
WHISK,
MINIMAL,
@ -523,7 +522,6 @@ 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_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)

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

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

View File

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

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