move attestation index outside signed message

This commit is contained in:
dapplion 2023-10-31 17:17:08 +02:00
parent b2f2102dad
commit e8e00f332a
12 changed files with 102 additions and 43 deletions

View File

@ -98,8 +98,8 @@ get_matching_head_attestations = cache_this(
_get_attesting_indices = get_attesting_indices _get_attesting_indices = get_attesting_indices
get_attesting_indices = cache_this( get_attesting_indices = cache_this(
lambda state, data, bits: ( lambda state, attestation: (
state.randao_mixes.hash_tree_root(), 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)''' _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)'''

View File

@ -401,7 +401,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge
# Verify responder is slashable # Verify responder is slashable
assert is_slashable_validator(responder, get_current_epoch(state)) assert is_slashable_validator(responder, get_current_epoch(state))
# Verify the responder participated in the attestation # 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 assert challenge.responder_index in attesters
# Verify shard transition is correctly given # Verify shard transition is correctly given
assert hash_tree_root(challenge.shard_transition) == challenge.attestation.data.shard_transition_root 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 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] assert hash_tree_root(custody_slashing.data) == shard_transition.shard_data_roots[custody_slashing.data_index]
# Verify existence and participation of claimed malefactor # 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 assert custody_slashing.malefactor_index in attesters
# Verify the malefactor custody key # Verify the malefactor custody key

View File

@ -0,0 +1,64 @@
# 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)
- [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)
<!-- 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.
## 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
```

View File

@ -496,7 +496,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
epoch_participation = state.previous_epoch_participation epoch_participation = state.previous_epoch_participation
proposer_reward_numerator = 0 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): 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): 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) epoch_participation[index] = add_flag(epoch_participation[index], flag_index)

View File

@ -69,7 +69,7 @@ def translate_participation(state: BeaconState, pending_attestations: Sequence[p
# Apply flags to all attesting validators # Apply flags to all attesting validators
epoch_participation = state.previous_epoch_participation 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: for flag_index in participation_flag_indices:
epoch_participation[index] = add_flag(epoch_participation[index], flag_index) epoch_participation[index] = add_flag(epoch_participation[index], flag_index)

View File

@ -316,12 +316,13 @@ def verify_and_notify_new_payload(self: ExecutionEngine,
```python ```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None: def process_attestation(state: BeaconState, attestation: Attestation) -> None:
data = attestation.data 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 in (get_previous_epoch(state), get_current_epoch(state))
assert data.target.epoch == compute_epoch_at_slot(data.slot) 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.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) assert len(attestation.aggregation_bits) == len(committee)
# Participation flag indices # Participation flag indices
@ -337,7 +338,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
epoch_participation = state.previous_epoch_participation epoch_participation = state.previous_epoch_participation
proposer_reward_numerator = 0 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): 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): 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) epoch_participation[index] = add_flag(epoch_participation[index], flag_index)

View File

@ -100,6 +100,7 @@
- [`get_domain`](#get_domain) - [`get_domain`](#get_domain)
- [`get_indexed_attestation`](#get_indexed_attestation) - [`get_indexed_attestation`](#get_indexed_attestation)
- [`get_attesting_indices`](#get_attesting_indices) - [`get_attesting_indices`](#get_attesting_indices)
- [`get_attestation_index`](#get_attestation_index)
- [Beacon state mutators](#beacon-state-mutators) - [Beacon state mutators](#beacon-state-mutators)
- [`increase_balance`](#increase_balance) - [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_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``. 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( return IndexedAttestation(
attesting_indices=sorted(attesting_indices), attesting_indices=sorted(attesting_indices),
@ -1094,14 +1095,19 @@ def get_indexed_attestation(state: BeaconState, attestation: Attestation) -> Ind
#### `get_attesting_indices` #### `get_attesting_indices`
```python ```python
def get_attesting_indices(state: BeaconState, def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]:
data: AttestationData,
bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Set[ValidatorIndex]:
""" """
Return the set of attesting indices corresponding to ``data`` and ``bits``. Return the set of attesting indices corresponding to ``data`` and ``bits``.
""" """
committee = get_beacon_committee(state, data.slot, data.index) committee = get_beacon_committee(state, attestation.data.slot, get_attestation_index(attestation))
return set(index for i, index in enumerate(committee) if bits[i]) 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 ### Beacon state mutators
@ -1339,7 +1345,7 @@ def get_unslashed_attesting_indices(state: BeaconState,
attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]: attestations: Sequence[PendingAttestation]) -> Set[ValidatorIndex]:
output = set() # type: Set[ValidatorIndex] output = set() # type: Set[ValidatorIndex]
for a in attestations: 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)) 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): for index in get_unslashed_attesting_indices(state, matching_source_attestations):
attestation = min([ attestation = min([
a for a in matching_source_attestations 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) ], key=lambda a: a.inclusion_delay)
rewards[attestation.proposer_index] += get_proposer_reward(state, index) rewards[attestation.proposer_index] += get_proposer_reward(state, index)
max_attester_reward = Gwei(get_base_reward(state, index) - get_proposer_reward(state, index)) max_attester_reward = Gwei(get_base_reward(state, index) - get_proposer_reward(state, index))

View File

@ -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. 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. 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) -- - _[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` 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). (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). (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 - _[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`. with index `aggregate_and_proof.aggregator_index` for the epoch `aggregate.data.target.epoch`.
- _[REJECT]_ The attestation has participants -- - _[REJECT]_ The attestation has participants -- that is, `len(get_attesting_indices(state, aggregate)) >= 1`.
that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1`.
- _[REJECT]_ `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- - _[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 -- - _[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 - _[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`. 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. - _[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`. 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. 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 -- - _[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)`, 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. 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 - _[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 -- - _[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 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. - _[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 - _[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. that has an identical `attestation.data.target.epoch` and participating validator index.
- _[REJECT]_ The signature of `attestation` is valid. - _[REJECT]_ The signature of `attestation` is valid.

View File

@ -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`. - 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 ##### Aggregate signature

View File

@ -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) attestation_data = build_attestation_data(spec, state, slot=slot, index=index, beacon_block_root=beacon_block_root)
beacon_committee = spec.get_beacon_committee( beacon_committee = spec.get_beacon_committee(state, slot, index)
state,
attestation_data.slot,
attestation_data.index,
)
committee_size = len(beacon_committee) committee_size = len(beacon_committee)
aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*([0] * committee_size)) 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): def sign_attestation(spec, state, attestation):
participants = spec.get_attesting_indices( participants = spec.get_attesting_indices(state, attestation)
state,
attestation.data,
attestation.aggregation_bits,
)
attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, participants) 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( beacon_committee = spec.get_beacon_committee(
state, state,
attestation.data.slot, attestation.data.slot,
attestation.data.index, spec.get_attestation_index(attestation),
) )
# By default, have everyone participate # By default, have everyone participate
participants = set(beacon_committee) participants = set(beacon_committee)

View File

@ -201,7 +201,7 @@ def run_get_inclusion_delay_deltas(spec, state):
# Track proposer of earliest included attestation for the validator defined by index # Track proposer of earliest included attestation for the validator defined by index
earliest_attestation = min([ earliest_attestation = min([
a for a in eligible_attestations 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) ], key=lambda a: a.inclusion_delay)
rewarded_proposer_indices.add(earliest_attestation.proposer_index) rewarded_proposer_indices.add(earliest_attestation.proposer_index)

View File

@ -236,11 +236,7 @@ def test_invalid_future_target_epoch(spec, state):
attestation = get_valid_attestation(spec, state) attestation = get_valid_attestation(spec, state)
participants = spec.get_attesting_indices( participants = spec.get_attesting_indices(state, attestation)
state,
attestation.data,
attestation.aggregation_bits
)
attestation.data.target.epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle attestation.data.target.epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle
# manually add signature for correct participants # manually add signature for correct participants