From d1bedbfbec74828eccdaee9ac9d9d91ddb0b1386 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 7 May 2021 11:05:27 -0700 Subject: [PATCH] Update inactivity penalty deltas processing Two main changes: 1. Inactivity scores are modified to decrease slowly for inactive validators when we are not in a leak, and quickly for active validators 2. The inactivity penalties are applied even not during a leak (note that inactivity _scores_ decrease when outside of a leak) This has the effect that the inactivity leak "overshoots" the target of finalizing again, and keeps leaking balances a bit more. For inactive validators, this PR sets post-leak recovery to happen 3x faster than the during-leak increase, so if a validator loses 3% during a leak, if they stay offline they should expect to lose another 1% until their score decreases back to zero. --- specs/altair/beacon-chain.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index b2ee93557..681d4aad0 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -120,6 +120,7 @@ This patch updates a few configuration values to move penalty parameters closer | - | - | | `SYNC_COMMITTEE_SIZE` | `uint64(2**9)` (= 512) | | `INACTIVITY_SCORE_BIAS` | `uint64(4)` | +| `INACTIVITY_SCORE_RECOVERY_RATE` | `uint64(16)` | ### Time parameters @@ -397,17 +398,16 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S """ rewards = [Gwei(0) for _ in range(len(state.validators))] penalties = [Gwei(0) for _ in range(len(state.validators))] - if is_in_inactivity_leak(state): - previous_epoch = get_previous_epoch(state) - matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) - for index in get_eligible_validator_indices(state): - for (_, weight) in get_flag_indices_and_weights(): - # This inactivity penalty cancels the flag reward corresponding to the flag index - penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) - if index not in matching_target_indices: - penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] - penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR - penalties[index] += Gwei(penalty_numerator // penalty_denominator) + previous_epoch = get_previous_epoch(state) + matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) + for index in get_eligible_validator_indices(state): + for (_, weight) in get_flag_indices_and_weights(): + # This inactivity penalty cancels the flag reward corresponding to the flag index + penalties[index] += Gwei(get_base_reward(state, index) * weight // WEIGHT_DENOMINATOR) + if index not in matching_target_indices: + penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] + penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR + penalties[index] += Gwei(penalty_numerator // penalty_denominator) return rewards, penalties ``` @@ -621,11 +621,14 @@ def process_justification_and_finalization(state: BeaconState) -> None: ```python def process_inactivity_updates(state: BeaconState) -> None: for index in get_eligible_validator_indices(state): + # Increase inactivity score of inactive validators if index in get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)): - if state.inactivity_scores[index] > 0: - state.inactivity_scores[index] -= 1 - elif is_in_inactivity_leak(state): + state.inactivity_scores[index] -= min(1, state.inactivity_scores[index]) + else: state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS + # Decrease the score of all validators for forgiveness when not during a leak + if not if is_in_inactivity_leak(state): + state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index]) ``` #### Rewards and penalties