From e8e00f332a85cad650dc629ed29f948736a5157d Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 31 Oct 2023 17:17:08 +0200 Subject: [PATCH 1/6] move attestation index outside signed message --- pysetup/spec_builders/phase0.py | 4 +- specs/_features/custody_game/beacon-chain.md | 4 +- specs/_features/eip7549/beacon-chain.md | 64 +++++++++++++++++++ specs/altair/beacon-chain.md | 2 +- specs/altair/fork.md | 2 +- specs/deneb/beacon-chain.md | 7 +- specs/phase0/beacon-chain.md | 22 ++++--- specs/phase0/p2p-interface.md | 16 ++--- specs/phase0/validator.md | 2 +- .../eth2spec/test/helpers/attestations.py | 14 +--- .../pyspec/eth2spec/test/helpers/rewards.py | 2 +- .../test_process_attestation.py | 6 +- 12 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 specs/_features/eip7549/beacon-chain.md diff --git a/pysetup/spec_builders/phase0.py b/pysetup/spec_builders/phase0.py index 6b3d82617..abfc8a802 100644 --- a/pysetup/spec_builders/phase0.py +++ b/pysetup/spec_builders/phase0.py @@ -98,8 +98,8 @@ get_matching_head_attestations = cache_this( _get_attesting_indices = get_attesting_indices get_attesting_indices = cache_this( - lambda state, data, bits: ( + lambda state, attestation: ( state.randao_mixes.hash_tree_root(), - state.validators.hash_tree_root(), data.hash_tree_root(), bits.hash_tree_root() + state.validators.hash_tree_root(), attestation.hash_tree_root() ), _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)''' diff --git a/specs/_features/custody_game/beacon-chain.md b/specs/_features/custody_game/beacon-chain.md index fd00dedda..e4effa2e1 100644 --- a/specs/_features/custody_game/beacon-chain.md +++ b/specs/_features/custody_game/beacon-chain.md @@ -401,7 +401,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge # Verify responder is slashable assert is_slashable_validator(responder, get_current_epoch(state)) # Verify the responder participated in the attestation - attesters = get_attesting_indices(state, challenge.attestation.data, challenge.attestation.aggregation_bits) + attesters = get_attesting_indices(state, challenge) assert challenge.responder_index in attesters # Verify shard transition is correctly given assert hash_tree_root(challenge.shard_transition) == challenge.attestation.data.shard_transition_root @@ -594,7 +594,7 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed assert len(custody_slashing.data) == shard_transition.shard_block_lengths[custody_slashing.data_index] assert hash_tree_root(custody_slashing.data) == shard_transition.shard_data_roots[custody_slashing.data_index] # Verify existence and participation of claimed malefactor - attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) + attesters = get_attesting_indices(state, attestation) assert custody_slashing.malefactor_index in attesters # Verify the malefactor custody key diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md new file mode 100644 index 000000000..a29164ecd --- /dev/null +++ b/specs/_features/eip7549/beacon-chain.md @@ -0,0 +1,64 @@ +# EIP-7549 -- The Beacon Chain + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Containers](#containers) + - [Extended containers](#extended-containers) + - [AttestationData](#attestationdata) + - [Attestation](#attestation) +- [Helper functions](#helper-functions) + - [Beacon state accessors](#beacon-state-accessors) + - [Modified `get_attestation_index`](#modified-get_attestation_index) + + + + +## 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. + +## Containers + +### Extended containers + +#### AttestationData + +```python +class AttestationData(Container): + slot: Slot + # index: CommitteeIndex # [Modified in EIP7549] + # LMD GHOST vote + beacon_block_root: Root + # FFG vote + source: Checkpoint + target: Checkpoint +``` + +#### Attestation + +```python +class Attestation(Container): + aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] + data: AttestationData + index: CommitteeIndex # [New in EIP7549] + signature: BLSSignature +``` + +## Helper functions + +### Beacon state accessors + +#### Modified `get_attestation_index` + +```python +def get_attestation_index(attestation: Attestation) -> CommitteeIndex: + return attestation.index +``` + diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 1de39d6fc..4bad3b556 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -496,7 +496,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: epoch_participation = state.previous_epoch_participation proposer_reward_numerator = 0 - for index in get_attesting_indices(state, data, attestation.aggregation_bits): + 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) diff --git a/specs/altair/fork.md b/specs/altair/fork.md index 60b048abd..a25050a17 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -69,7 +69,7 @@ def translate_participation(state: BeaconState, pending_attestations: Sequence[p # Apply flags to all attesting validators epoch_participation = state.previous_epoch_participation - for index in get_attesting_indices(state, data, attestation.aggregation_bits): + for index in get_attesting_indices(state, attestation): for flag_index in participation_flag_indices: epoch_participation[index] = add_flag(epoch_participation[index], flag_index) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index b6c97d6f4..01523baf5 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -316,12 +316,13 @@ def verify_and_notify_new_payload(self: ExecutionEngine, ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: data = attestation.data + index = get_attestation_index(attestation) 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 Deneb:EIP7045] - assert data.index < get_committee_count_per_slot(state, data.target.epoch) + assert index < get_committee_count_per_slot(state, data.target.epoch) - committee = get_beacon_committee(state, data.slot, data.index) + committee = get_beacon_committee(state, data.slot, index) assert len(attestation.aggregation_bits) == len(committee) # Participation flag indices @@ -337,7 +338,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: epoch_participation = state.previous_epoch_participation proposer_reward_numerator = 0 - for index in get_attesting_indices(state, data, attestation.aggregation_bits): + 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) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index bdfb07838..3427a9630 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -100,6 +100,7 @@ - [`get_domain`](#get_domain) - [`get_indexed_attestation`](#get_indexed_attestation) - [`get_attesting_indices`](#get_attesting_indices) + - [`get_attestation_index`](#get_attestation_index) - [Beacon state mutators](#beacon-state-mutators) - [`increase_balance`](#increase_balance) - [`decrease_balance`](#decrease_balance) @@ -1082,7 +1083,7 @@ def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> Ind """ Return the indexed attestation corresponding to ``attestation``. """ - attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) + attesting_indices = get_attesting_indices(state, attestation) return IndexedAttestation( attesting_indices=sorted(attesting_indices), @@ -1094,14 +1095,19 @@ def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> Ind #### `get_attesting_indices` ```python -def get_attesting_indices(state: BeaconState, - data: AttestationData, - bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]: +def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: """ Return the set of attesting indices corresponding to ``data`` and ``bits``. """ - committee = get_beacon_committee(state, data.slot, data.index) - return set(index for i, index in enumerate(committee) if bits[i]) + committee = get_beacon_committee(state, attestation.data.slot, get_attestation_index(attestation)) + return set(index for i, index in enumerate(committee) if attestation.aggregation_bits[i]) +``` + +#### `get_attestation_index` + +```python +def get_attestation_index(attestation: Attestation) -> CommitteeIndex: + return attestation.data.index ``` ### Beacon state mutators @@ -1339,7 +1345,7 @@ def get_unslashed_attesting_indices(state: BeaconState, attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: output = set() # type: Set[ValidatorIndex] for a in attestations: - output = output.union(get_attesting_indices(state, a.data, a.aggregation_bits)) + output = output.union(get_attesting_indices(state, a)) return set(filter(lambda index: not state.validators[index].slashed, output)) ``` @@ -1512,7 +1518,7 @@ def get_inclusion_delay_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequ for index in get_unslashed_attesting_indices(state, matching_source_attestations): attestation = min([ a for a in matching_source_attestations - if index in get_attesting_indices(state, a.data, a.aggregation_bits) + if index in get_attesting_indices(state, a) ], key=lambda a: a.inclusion_delay) rewards[attestation.proposer_index] += get_proposer_reward(state, index) max_attester_reward = Gwei(get_base_reward(state, index) - get_proposer_reward(state, index)) diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index e9d43e5a7..dbe064739 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -354,7 +354,7 @@ The `beacon_aggregate_and_proof` topic is used to propagate aggregated attestati to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. -(We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message` and `aggregate = aggregate_and_proof.aggregate`) +(We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message`, `aggregate = aggregate_and_proof.aggregate` and `index = get_attestation_index(aggregate)`) - _[IGNORE]_ `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` (a client MAY queue future aggregates for processing at the appropriate slot). @@ -364,12 +364,11 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ (via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally). - _[IGNORE]_ The `aggregate` is the first valid aggregate received for the aggregator with index `aggregate_and_proof.aggregator_index` for the epoch `aggregate.data.target.epoch`. -- _[REJECT]_ The attestation has participants -- - that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1`. +- _[REJECT]_ The attestation has participants -- that is, `len(get_attesting_indices(state, aggregate)) >= 1`. - _[REJECT]_ `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- - i.e. `is_aggregator(state, aggregate.data.slot, aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`. + i.e. `is_aggregator(state, aggregate.data.slot, index, aggregate_and_proof.selection_proof)` returns `True`. - _[REJECT]_ The aggregator's validator index is within the committee -- - i.e. `aggregate_and_proof.aggregator_index in get_beacon_committee(state, aggregate.data.slot, aggregate.data.index)`. + i.e. `aggregate_and_proof.aggregator_index in get_beacon_committee(state, aggregate.data.slot, index)`. - _[REJECT]_ The `aggregate_and_proof.selection_proof` is a valid signature of the `aggregate.data.slot` by the validator with index `aggregate_and_proof.aggregator_index`. - _[REJECT]_ The aggregator signature, `signed_aggregate_and_proof.signature`, is valid. @@ -425,9 +424,10 @@ The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated a to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`. The following validations MUST pass before forwarding the `attestation` on the subnet. -- _[REJECT]_ The committee index is within the expected range -- i.e. `data.index < get_committee_count_per_slot(state, data.target.epoch)`. +(We define the following for convenience -- `index = get_attestation_index(attestation)`) +- _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, data.target.epoch)`. - _[REJECT]_ The attestation is for the correct subnet -- - i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, attestation.data.index) == subnet_id`, + i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, index) == subnet_id`, where `committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch)`, which may be pre-computed along with the committee information for the signature check. - _[IGNORE]_ `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots @@ -439,7 +439,7 @@ The following validations MUST pass before forwarding the `attestation` on the s - _[REJECT]_ The attestation is unaggregated -- that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set). - _[REJECT]_ The number of aggregation bits matches the committee size -- i.e. - `len(attestation.aggregation_bits) == len(get_beacon_committee(state, data.slot, data.index))`. + `len(attestation.aggregation_bits) == len(get_beacon_committee(state, data.slot, index))`. - _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet that has an identical `attestation.data.target.epoch` and participating validator index. - _[REJECT]_ The signature of `attestation` is valid. diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 533cdeabc..6d16ec605 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -494,7 +494,7 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes - Let `attestation.aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. -*Note*: Calling `get_attesting_indices(state, attestation.data, attestation.aggregation_bits)` should return a list of length equal to 1, containing `validator_index`. +*Note*: Calling `get_attesting_indices(state, attestation)` should return a list of length equal to 1, containing `validator_index`. ##### Aggregate signature diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 6cd35c538..2944f7e51 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -104,11 +104,7 @@ def get_valid_attestation(spec, attestation_data = build_attestation_data(spec, state, slot=slot, index=index, beacon_block_root=beacon_block_root) - beacon_committee = spec.get_beacon_committee( - state, - attestation_data.slot, - attestation_data.index, - ) + beacon_committee = spec.get_beacon_committee(state, slot, index) committee_size = len(beacon_committee) aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size)) @@ -144,11 +140,7 @@ def sign_indexed_attestation(spec, state, indexed_attestation): def sign_attestation(spec, state, attestation): - participants = spec.get_attesting_indices( - state, - attestation.data, - attestation.aggregation_bits, - ) + participants = spec.get_attesting_indices(state, attestation) attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, participants) @@ -175,7 +167,7 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False, filter_pa beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, - attestation.data.index, + spec.get_attestation_index(attestation), ) # By default, have everyone participate participants = set(beacon_committee) diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index 03804d574..7e08a4ad9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -201,7 +201,7 @@ def run_get_inclusion_delay_deltas(spec, state): # Track proposer of earliest included attestation for the validator defined by index earliest_attestation = min([ a for a in eligible_attestations - if index in spec.get_attesting_indices(state, a.data, a.aggregation_bits) + if index in spec.get_attesting_indices(state, a) ], key=lambda a: a.inclusion_delay) rewarded_proposer_indices.add(earliest_attestation.proposer_index) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py index 94a5481fa..2cf314dc5 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attestation.py @@ -236,11 +236,7 @@ def test_invalid_future_target_epoch(spec, state): attestation = get_valid_attestation(spec, state) - participants = spec.get_attesting_indices( - state, - attestation.data, - attestation.aggregation_bits - ) + participants = spec.get_attesting_indices(state, attestation) attestation.data.target.epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle # manually add signature for correct participants From 38f269ca12c9cc1321cb0b613daa0527ee73b507 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:46:45 +0800 Subject: [PATCH 2/6] Update spec with @mkalinin suggestions --- specs/_features/eip7549/beacon-chain.md | 128 +++++++++++++++++++---- specs/_features/eip7549/p2p-interface.md | 54 ++++++++++ specs/_features/eip7549/validator.md | 49 +++++++++ specs/deneb/beacon-chain.md | 5 +- specs/phase0/beacon-chain.md | 10 +- specs/phase0/p2p-interface.md | 20 ++-- 6 files changed, 225 insertions(+), 41 deletions(-) create mode 100644 specs/_features/eip7549/p2p-interface.md create mode 100644 specs/_features/eip7549/validator.md diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index a29164ecd..f2936ebb4 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -7,13 +7,19 @@ - [Introduction](#introduction) +- [Preset](#preset) - [Containers](#containers) - - [Extended containers](#extended-containers) - - [AttestationData](#attestationdata) + - [Modified containers](#modified-containers) - [Attestation](#attestation) + - [IndexedAttestation](#indexedattestation) - [Helper functions](#helper-functions) + - [Misc](#misc) + - [`get_committee_indices`](#get_committee_indices) - [Beacon state accessors](#beacon-state-accessors) - - [Modified `get_attestation_index`](#modified-get_attestation_index) + - [New `get_committee_attesters`](#new-get_committee_attesters) + - [Modified `get_attesting_indices`](#modified-get_attesting_indices) + - [Block processing](#block-processing) + - [Modified `process_attestation`](#modified-process_attestation) @@ -24,41 +30,117 @@ This is the beacon chain specification to move the attestation committee index o *Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. +## Preset + +| Name | Value | Description | +| - | - | - | +| `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) | +| `MAX_ATTESTATIONS` | `2**3` (= 8) | + ## Containers -### Extended containers - -#### AttestationData - -```python -class AttestationData(Container): - slot: Slot - # index: CommitteeIndex # [Modified in EIP7549] - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint -``` +### Modified containers #### Attestation ```python class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] + aggregation_bits: List[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): + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] data: AttestationData - index: CommitteeIndex # [New in EIP7549] signature: BLSSignature ``` ## Helper functions -### Beacon state accessors +### Misc -#### Modified `get_attestation_index` +#### `get_committee_indices` ```python -def get_attestation_index(attestation: Attestation) -> CommitteeIndex: - return attestation.index +def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: + return [CommitteeIndex(index) for bit, index in enumerate(commitee_bits) if bit] ``` +### Beacon state accessors + +#### New `get_committee_attesters` + +```python +def get_committee_attesters(state: BeaconState, attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: + committee = get_beacon_committee(state, data.slot, index) + return set(index for i, index in enumerate(committee) if attesting_bits[i]) +``` + +#### 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() + committee_indices = get_committee_indices(attestation.committee_bits) + for index in committee_indices: + attesting_bits = attestation.attesting_bits[index] + committee_attesters = get_committee_attesters(state, attesting_bits, index) + output = output.union(committee_attesters) + + 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) + assert len(committee_indices) > 0 + assert len(committee_indices) == len(attestation.aggregation_bits) + for index in committee_indices: + assert index < get_committee_count_per_slot(state, data.target.epoch) + committee = get_beacon_committee(state, data.slot, index) + assert len(attestation.aggregation_bits[index]) == len(committee) + + # 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/p2p-interface.md b/specs/_features/eip7549/p2p-interface.md new file mode 100644 index 000000000..c7413ea4b --- /dev/null +++ b/specs/_features/eip7549/p2p-interface.md @@ -0,0 +1,54 @@ +# EIP-7549 -- Networking + +This document contains the consensus-layer networking specification for EIP-7549. + +The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. + +## Table of contents + + + + + +- [Modifications in EIP-7549](#modifications-in-eip-7549) + - [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) + + + + +## Modifications in EIP-7549 + +### The gossip domain: gossipsub + +#### 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. + +##### Global topics + +###### `beacon_aggregate_and_proof` + +*[Modified in EIP7549]* + +The following convenience variables are re-defined +- `index = get_committee_indices(aggregate.committee_bits)[0]` +- `aggregation_bits = aggregate.aggregation_bits[0]` + +The following validations are added: +* [REJECT] `len(committee_indices) == len(aggregate.attestation_bits) == 1`, where `committee_indices = get_committee_indices(aggregate)`. +* [REJECT] `aggregate.data.index == 0` + +###### `beacon_attestation_{subnet_id}` + +The following convenience variables are re-defined +- `index = get_committee_indices(attestation.committee_bits)[0]` +- `aggregation_bits = attestation.aggregation_bits[0]` + +The following validations are added: +* [REJECT] `len(committee_indices) == len(attestation.attestation_bits) == 1`, where `committee_indices = get_committee_indices(attestation)`. +* [REJECT] `attestation.data.index == 0` + diff --git a/specs/_features/eip7549/validator.md b/specs/_features/eip7549/validator.md new file mode 100644 index 000000000..1efe3bfbc --- /dev/null +++ b/specs/_features/eip7549/validator.md @@ -0,0 +1,49 @@ +# Deneb -- Honest Validator + +## Table of contents + + + + + +- [Modifications in EIP-7549](#modifications-in-eip-7549) + - [Block proposal](#block-proposal) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [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` + +##### Attestations + +Attestations received from aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. + +### Attesting + +#### Construct attestation + +- Set `attestation_data.index = 0`. +- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. +- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 + +*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]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. +- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 + + diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 01523baf5..57badd6b9 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -316,13 +316,12 @@ def verify_and_notify_new_payload(self: ExecutionEngine, ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: data = attestation.data - index = get_attestation_index(attestation) 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 Deneb:EIP7045] - assert index < get_committee_count_per_slot(state, data.target.epoch) + assert data.index < get_committee_count_per_slot(state, data.target.epoch) - committee = get_beacon_committee(state, data.slot, index) + committee = get_beacon_committee(state, data.slot, data.index) assert len(attestation.aggregation_bits) == len(committee) # Participation flag indices diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index d4c5b7f6c..9d5f8446a 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -100,7 +100,6 @@ - [`get_domain`](#get_domain) - [`get_indexed_attestation`](#get_indexed_attestation) - [`get_attesting_indices`](#get_attesting_indices) - - [`get_attestation_index`](#get_attestation_index) - [Beacon state mutators](#beacon-state-mutators) - [`increase_balance`](#increase_balance) - [`decrease_balance`](#decrease_balance) @@ -1103,17 +1102,10 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V """ Return the set of attesting indices corresponding to ``data`` and ``bits``. """ - committee = get_beacon_committee(state, attestation.data.slot, get_attestation_index(attestation)) + committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index) return set(index for i, index in enumerate(committee) if attestation.aggregation_bits[i]) ``` -#### `get_attestation_index` - -```python -def get_attestation_index(attestation: Attestation) -> CommitteeIndex: - return attestation.data.index -``` - ### Beacon state mutators #### `increase_balance` diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 9a7efdaab..a34b34233 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -353,8 +353,13 @@ The following validations MUST pass before forwarding the `signed_beacon_block` The `beacon_aggregate_and_proof` topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. +We define the following variables for convenience: +- `aggregate_and_proof = signed_aggregate_and_proof.message` +- `aggregate = aggregate_and_proof.aggregate` +- `index = aggregate.index` +- `aggregation_bits = attestation.aggregation_bits` + The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. -(We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message`, `aggregate = aggregate_and_proof.aggregate` and `index = get_attestation_index(aggregate)`) - _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, aggregate.data.target.epoch)`. - _[IGNORE]_ `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` @@ -362,9 +367,9 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ - _[REJECT]_ The aggregate attestation's epoch matches its target -- i.e. `aggregate.data.target.epoch == compute_epoch_at_slot(aggregate.data.slot)` - _[REJECT]_ The number of aggregation bits matches the committee size -- i.e. - `len(aggregate.aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, aggregate.data.index))`. + `len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, aggregate.data.index))`. - _[REJECT]_ The aggregate attestation has participants -- - that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1`. + that is, `len(get_attesting_indices(state, aggregate)) >= 1`. - _[IGNORE]_ A valid aggregate attestation defined by `hash_tree_root(aggregate.data)` whose `aggregation_bits` is a non-strict superset has _not_ already been seen. (via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally). - _[IGNORE]_ The `aggregate` is the first valid aggregate received for the aggregator @@ -430,8 +435,11 @@ Attestation subnets are used to propagate unaggregated attestations to subsectio The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated attestations to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`. +We define the following variables for convenience: +- `index = attestation.index` +- `aggregation_bits = attestation.aggregation_bits` + The following validations MUST pass before forwarding the `attestation` on the subnet. -(We define the following for convenience -- `index = get_attestation_index(attestation)`) - _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, attestation.data.target.epoch)`. - _[REJECT]_ The attestation is for the correct subnet -- i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, index) == subnet_id`, @@ -444,9 +452,9 @@ The following validations MUST pass before forwarding the `attestation` on the s - _[REJECT]_ The attestation's epoch matches its target -- i.e. `attestation.data.target.epoch == compute_epoch_at_slot(attestation.data.slot)` - _[REJECT]_ The attestation is unaggregated -- - that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set). + that is, it has exactly one participating validator (`len([bit for bit in aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set). - _[REJECT]_ The number of aggregation bits matches the committee size -- i.e. - `len(attestation.aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, index))`. + `len(aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, index))`. - _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet that has an identical `attestation.data.target.epoch` and participating validator index. - _[REJECT]_ The signature of `attestation` is valid. From dc37dcdbf23844faa92e40c6d0af59cf89e29aa3 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Mar 2024 23:03:55 +0800 Subject: [PATCH 3/6] fix broken tests --- tests/core/pyspec/eth2spec/test/helpers/attestations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index ea84adbe7..9e549a74b 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -167,7 +167,7 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False, filter_pa beacon_committee = spec.get_beacon_committee( state, attestation.data.slot, - spec.get_attestation_index(attestation), + attestation.data.index, ) # By default, have everyone participate participants = set(beacon_committee) From 03c23c6fb1a9ea7fadd8340db6d1743b3318c29e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Mar 2024 23:24:21 +0800 Subject: [PATCH 4/6] make it executable for basic linter checks & fix errors --- .gitignore | 1 + pysetup/constants.py | 1 + pysetup/md_doc_paths.py | 4 +++- pysetup/spec_builders/__init__.py | 3 ++- pysetup/spec_builders/eip7549.py | 11 +++++++++++ specs/_features/eip7549/beacon-chain.md | 22 ++++++++++++---------- 6 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 pysetup/spec_builders/eip7549.py diff --git a/.gitignore b/.gitignore index 474db02c5..205bebcd9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ tests/core/pyspec/eth2spec/capella/ tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/eip6110/ tests/core/pyspec/eth2spec/eip7002/ +tests/core/pyspec/eth2spec/eip7549/ tests/core/pyspec/eth2spec/whisk/ tests/core/pyspec/eth2spec/eip7594/ diff --git a/pysetup/constants.py b/pysetup/constants.py index 238b3b02c..85d347a54 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -6,6 +6,7 @@ CAPELLA = 'capella' DENEB = 'deneb' EIP6110 = 'eip6110' EIP7002 = 'eip7002' +EIP7549 = 'eip7549' WHISK = 'whisk' EIP7594 = 'eip7594' diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index c93c0c032..675f700d5 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -7,8 +7,9 @@ from .constants import ( CAPELLA, DENEB, EIP6110, - WHISK, EIP7002, + EIP7549, + WHISK, EIP7594, ) @@ -20,6 +21,7 @@ PREVIOUS_FORK_OF = { CAPELLA: BELLATRIX, DENEB: CAPELLA, EIP6110: DENEB, + EIP7549: DENEB, WHISK: CAPELLA, EIP7002: CAPELLA, EIP7594: DENEB, diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index 061d11626..54040bffb 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -5,6 +5,7 @@ from .capella import CapellaSpecBuilder from .deneb import DenebSpecBuilder from .eip6110 import EIP6110SpecBuilder from .eip7002 import EIP7002SpecBuilder +from .eip7549 import EIP7549SpecBuilder from .whisk import WhiskSpecBuilder from .eip7594 import EIP7594SpecBuilder @@ -13,6 +14,6 @@ spec_builders = { builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - EIP6110SpecBuilder, EIP7002SpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, + EIP6110SpecBuilder, EIP7002SpecBuilder, EIP7549SpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, ) } diff --git a/pysetup/spec_builders/eip7549.py b/pysetup/spec_builders/eip7549.py new file mode 100644 index 000000000..bd5dbbeae --- /dev/null +++ b/pysetup/spec_builders/eip7549.py @@ -0,0 +1,11 @@ +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 index f2936ebb4..9280e165e 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -41,21 +41,22 @@ This is the beacon chain specification to move the attestation committee index o ### Modified containers -#### Attestation +#### `Attestation` ```python class Attestation(Container): - aggregation_bits: List[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 + aggregation_bits: List[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 +#### `IndexedAttestation` ```python class IndexedAttestation(Container): - attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] + # [Modified in EIP7549] + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] data: AttestationData signature: BLSSignature ``` @@ -68,7 +69,7 @@ class IndexedAttestation(Container): ```python def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: - return [CommitteeIndex(index) for bit, index in enumerate(commitee_bits) if bit] + return [CommitteeIndex(index) for bit, index in enumerate(commitee_bits) if bit] ``` ### Beacon state accessors @@ -76,7 +77,8 @@ def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: #### New `get_committee_attesters` ```python -def get_committee_attesters(state: BeaconState, attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: +def get_committee_attesters(state: BeaconState, data: AttestationData, + attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: committee = get_beacon_committee(state, data.slot, index) return set(index for i, index in enumerate(committee) if attesting_bits[i]) ``` @@ -93,7 +95,7 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V committee_indices = get_committee_indices(attestation.committee_bits) for index in committee_indices: attesting_bits = attestation.attesting_bits[index] - committee_attesters = get_committee_attesters(state, attesting_bits, index) + committee_attesters = get_committee_attesters(state, attestation.data, attesting_bits, index) output = output.union(committee_attesters) return output From 43dbf8cef5d57d526b70ab7cf776b33726dee2e6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 6 Mar 2024 23:28:07 +0800 Subject: [PATCH 5/6] Fix ToC and apply @mehdi-aouadi's suggestion --- specs/_features/eip7549/beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index 9280e165e..24a3386e8 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -10,8 +10,8 @@ - [Preset](#preset) - [Containers](#containers) - [Modified containers](#modified-containers) - - [Attestation](#attestation) - - [IndexedAttestation](#indexedattestation) + - [`Attestation`](#attestation) + - [`IndexedAttestation`](#indexedattestation) - [Helper functions](#helper-functions) - [Misc](#misc) - [`get_committee_indices`](#get_committee_indices) @@ -77,9 +77,9 @@ def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: #### New `get_committee_attesters` ```python -def get_committee_attesters(state: BeaconState, data: AttestationData, +def get_committee_attesters(state: BeaconState, slot: Slot, attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: - committee = get_beacon_committee(state, data.slot, index) + committee = get_beacon_committee(state, slot, index) return set(index for i, index in enumerate(committee) if attesting_bits[i]) ``` @@ -95,7 +95,7 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V committee_indices = get_committee_indices(attestation.committee_bits) for index in committee_indices: attesting_bits = attestation.attesting_bits[index] - committee_attesters = get_committee_attesters(state, attestation.data, attesting_bits, index) + committee_attesters = get_committee_attesters(state, attestation.data.slot, attesting_bits, index) output = output.union(committee_attesters) return output From 5f78d2b4361c5ceb3abb629f75a292d1eb046997 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:10:41 +0800 Subject: [PATCH 6/6] apply suggestions --- specs/_features/eip7549/beacon-chain.md | 16 +++------------- specs/_features/eip7549/validator.md | 4 +++- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index 24a3386e8..2382551e2 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -16,7 +16,6 @@ - [Misc](#misc) - [`get_committee_indices`](#get_committee_indices) - [Beacon state accessors](#beacon-state-accessors) - - [New `get_committee_attesters`](#new-get_committee_attesters) - [Modified `get_attesting_indices`](#modified-get_attesting_indices) - [Block processing](#block-processing) - [Modified `process_attestation`](#modified-process_attestation) @@ -74,15 +73,6 @@ def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: ### Beacon state accessors -#### New `get_committee_attesters` - -```python -def get_committee_attesters(state: BeaconState, slot: Slot, - attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: - committee = get_beacon_committee(state, slot, index) - return set(index for i, index in enumerate(committee) if attesting_bits[i]) -``` - #### Modified `get_attesting_indices` ```python @@ -94,8 +84,9 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V output = set() committee_indices = get_committee_indices(attestation.committee_bits) for index in committee_indices: - attesting_bits = attestation.attesting_bits[index] - committee_attesters = get_committee_attesters(state, attestation.data.slot, attesting_bits, index) + attesting_bits = attestation.aggregation_bits[index] + committee = get_beacon_committee(state, attestation.data.slot, index) + committee_attesters = set(index for i, index in enumerate(committee) if attesting_bits[i]) output = output.union(committee_attesters) return output @@ -115,7 +106,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # [Modified in EIP7549] assert data.index == 0 committee_indices = get_committee_indices(attestation.committee_bits) - assert len(committee_indices) > 0 assert len(committee_indices) == len(attestation.aggregation_bits) for index in committee_indices: assert index < get_committee_count_per_slot(state, data.target.epoch) diff --git a/specs/_features/eip7549/validator.md b/specs/_features/eip7549/validator.md index 1efe3bfbc..6ae84aca6 100644 --- a/specs/_features/eip7549/validator.md +++ b/specs/_features/eip7549/validator.md @@ -35,6 +35,8 @@ Attestations received from aggregators with disjoint `committee_bits` sets and e - Set `attestation_data.index = 0`. - Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. - Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 +- Let `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` +- Set `attestation.committee_bits = committee_bits` *Note*: Calling `get_attesting_indices(state, attestation)` should return a list of length equal to 1, containing `validator_index`. @@ -45,5 +47,5 @@ Attestations received from aggregators with disjoint `committee_bits` sets and e - Set `attestation_data.index = 0`. - Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. - Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 - +- Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the same value as in each individual attestation