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/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..2382551e2 --- /dev/null +++ b/specs/_features/eip7549/beacon-chain.md @@ -0,0 +1,138 @@ +# EIP-7549 -- The Beacon Chain + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Preset](#preset) +- [Containers](#containers) + - [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_attesting_indices`](#modified-get_attesting_indices) + - [Block processing](#block-processing) + - [Modified `process_attestation`](#modified-process_attestation) + + + + +## Introduction + +This is the beacon chain specification to move the attestation committee index outside of the signed message. For motivation, refer to [EIP-7549](https://eips.ethereum.org/EIPS/eip-7549). + +*Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. + +## Preset + +| Name | Value | Description | +| - | - | - | +| `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) | +| `MAX_ATTESTATIONS` | `2**3` (= 8) | + +## Containers + +### Modified containers + +#### `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 +``` + +#### `IndexedAttestation` + +```python +class IndexedAttestation(Container): + # [Modified in EIP7549] + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] + data: AttestationData + signature: BLSSignature +``` + +## Helper functions + +### Misc + +#### `get_committee_indices` + +```python +def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: + return [CommitteeIndex(index) for bit, index 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() + committee_indices = get_committee_indices(attestation.committee_bits) + for index in committee_indices: + 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 +``` + +### 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) == 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..6ae84aca6 --- /dev/null +++ b/specs/_features/eip7549/validator.md @@ -0,0 +1,51 @@ +# 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 +- 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`. + +### 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 +- Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the same value as in each individual attestation + 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..57badd6b9 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -337,7 +337,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 f5ac401e9..9d5f8446a 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1086,7 +1086,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), @@ -1098,14 +1098,12 @@ 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, attestation.data.index) + return set(index for i, index in enumerate(committee) if attestation.aggregation_bits[i]) ``` ### Beacon state mutators @@ -1343,7 +1341,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)) ``` @@ -1516,7 +1514,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 795c4aa2e..a34b34233 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -353,26 +353,32 @@ 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` and `aggregate = aggregate_and_proof.aggregate`) -- _[REJECT]_ The committee index is within the expected range -- i.e. `aggregate.data.index < get_committee_count_per_slot(state, aggregate.data.target.epoch)`. +- _[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` (a client MAY queue future aggregates for processing at the appropriate slot). - _[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 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)) >= 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. @@ -429,10 +435,14 @@ 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. -- _[REJECT]_ The committee index is within the expected range -- i.e. `attestation.data.index < get_committee_count_per_slot(state, attestation.data.target.epoch)`. +- _[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, 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 @@ -442,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, attestation.data.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. 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 7fecfd7cc..9e549a74b 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) 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