diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 7bf0c2d99..374bac5cf 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -37,6 +37,7 @@ - [`get_base_reward_per_increment`](#get_base_reward_per_increment) - [`get_base_reward`](#get_base_reward) - [`get_unslashed_participating_indices`](#get_unslashed_participating_indices) + - [`get_attestation_participation_flag_indices`](#get_attestation_participation_flag_indices) - [`get_flag_index_deltas`](#get_flag_index_deltas) - [Modified `get_inactivity_penalty_deltas`](#modified-get_inactivity_penalty_deltas) - [Beacon state mutators](#beacon-state-mutators) @@ -352,6 +353,37 @@ def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epo return set(filter(lambda index: not state.validators[index].slashed, participating_indices)) ``` +#### `get_attestation_participation_flag_indices` + +```python +def get_attestation_participation_flag_indices(state: BeaconState, + data: AttestationData, + inclusion_delay: uint64) -> Sequence[int]: + """ + Return the flag indices that are satisfied by an attestation. + """ + if data.target.epoch == get_current_epoch(state): + justified_checkpoint = state.current_justified_checkpoint + else: + justified_checkpoint = state.previous_justified_checkpoint + + # Matching roots + is_matching_source = data.source == justified_checkpoint + is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) + is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) + assert is_matching_source + + participation_flag_indices = [] + if is_matching_source and inclusion_delay <= integer_squareroot(SLOTS_PER_EPOCH): + participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX) + if is_matching_target and inclusion_delay <= SLOTS_PER_EPOCH: + participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX) + if is_matching_head and inclusion_delay == MIN_ATTESTATION_INCLUSION_DELAY: + participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX) + + return participation_flag_indices +``` + #### `get_flag_index_deltas` ```python @@ -463,32 +495,18 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: committee = get_beacon_committee(state, data.slot, data.index) assert len(attestation.aggregation_bits) == len(committee) - if data.target.epoch == get_current_epoch(state): - epoch_participation = state.current_epoch_participation - justified_checkpoint = state.current_justified_checkpoint - else: - epoch_participation = state.previous_epoch_participation - justified_checkpoint = state.previous_justified_checkpoint - - # Matching roots - is_matching_source = data.source == justified_checkpoint - is_matching_target = is_matching_source and data.target.root == get_block_root(state, data.target.epoch) - is_matching_head = is_matching_target and data.beacon_block_root == get_block_root_at_slot(state, data.slot) - assert is_matching_source + # 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)) - # Participation flag indices - participation_flag_indices = [] - if is_matching_source and state.slot <= data.slot + integer_squareroot(SLOTS_PER_EPOCH): - participation_flag_indices.append(TIMELY_SOURCE_FLAG_INDEX) - if is_matching_target and state.slot <= data.slot + SLOTS_PER_EPOCH: - participation_flag_indices.append(TIMELY_TARGET_FLAG_INDEX) - if is_matching_head and state.slot == data.slot + MIN_ATTESTATION_INCLUSION_DELAY: - participation_flag_indices.append(TIMELY_HEAD_FLAG_INDEX) - # 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, data, attestation.aggregation_bits): for flag_index, weight in get_flag_indices_and_weights(): diff --git a/specs/altair/fork.md b/specs/altair/fork.md index be562b82b..d8374d009 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -41,6 +41,21 @@ Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since After `process_slots` of Phase 0 finishes, if `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == ALTAIR_FORK_EPOCH`, an irregular state change is made to upgrade to Altair. ```python +def translate_participation(state: BeaconState, pending_attestations: Sequence[PendingAttestation]) -> None: + for attestation in pending_attestations: + data = attestation.data + inclusion_delay = attestation.inclusion_delay + # Translate attestation inclusion info to flag indices + participation_flag_indices = get_attestation_participation_flag_indices(state, data, inclusion_delay) + + # Apply flags to all attesting validators + epoch_participation = state.previous_epoch_participation + for index in get_attesting_indices(state, data, attestation.aggregation_bits): + for flag_index, weight in get_flag_indices_and_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) + + def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: epoch = phase0.get_current_epoch(pre) post = BeaconState( @@ -80,6 +95,8 @@ def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: # Inactivity inactivity_scores=[uint64(0) for _ in range(len(pre.validators))], ) + # Fill in previous epoch participation from the pre state's pending attestations + translate_participation(post, pre.previous_epoch_attestations) # Fill in sync committees post.current_sync_committee = get_sync_committee(post, get_current_epoch(post)) post.next_sync_committee = get_sync_committee(post, get_current_epoch(post) + EPOCHS_PER_SYNC_COMMITTEE_PERIOD)