Merge pull request #3559 from dapplion/attestation-index
EIP-7549: Move committee index outside Attestation
This commit is contained in:
commit
5d80b1954a
|
@ -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/
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ CAPELLA = 'capella'
|
|||
DENEB = 'deneb'
|
||||
EIP6110 = 'eip6110'
|
||||
EIP7002 = 'eip7002'
|
||||
EIP7549 = 'eip7549'
|
||||
WHISK = 'whisk'
|
||||
EIP7594 = 'eip7594'
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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'''
|
||||
'''
|
|
@ -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)'''
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
# 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)
|
||||
- [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` | `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)
|
||||
```
|
|
@ -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
|
||||
|
||||
<!-- 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)
|
||||
- [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)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## 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`
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# 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)
|
||||
- [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`
|
||||
|
||||
##### 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
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue