diff --git a/configs/mainnet/altair.yaml b/configs/mainnet/altair.yaml index d7daf6028..ee3785f14 100644 --- a/configs/mainnet/altair.yaml +++ b/configs/mainnet/altair.yaml @@ -4,7 +4,7 @@ CONFIG_NAME: "mainnet" # Updated penalty values # --------------------------------------------------------------- -# 3 * 2**24) (= 50,331,648) +# 3 * 2**24 (= 50,331,648) ALTAIR_INACTIVITY_PENALTY_QUOTIENT: 50331648 # 2**6 (= 64) ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT: 64 @@ -14,12 +14,12 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2 # Misc # --------------------------------------------------------------- -# 2**10 (=1,024) +# 2**10 (= 1,024) SYNC_COMMITTEE_SIZE: 1024 -# 2**6 (=64) +# 2**6 (= 64) SYNC_SUBCOMMITTEE_SIZE: 64 -# 2**2 (=4) -LEAK_SCORE_BIAS: 4 +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 # Time parameters diff --git a/configs/minimal/altair.yaml b/configs/minimal/altair.yaml index dc0491d60..a70c0404b 100644 --- a/configs/minimal/altair.yaml +++ b/configs/minimal/altair.yaml @@ -4,7 +4,7 @@ CONFIG_NAME: "minimal" # Updated penalty values # --------------------------------------------------------------- -# 3 * 2**24) (= 50,331,648) +# 3 * 2**24 (= 50,331,648) ALTAIR_INACTIVITY_PENALTY_QUOTIENT: 50331648 # 2**6 (= 64) ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT: 64 @@ -18,8 +18,8 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2 SYNC_COMMITTEE_SIZE: 32 # [customized] SYNC_SUBCOMMITTEE_SIZE: 16 -# 2**2 (=4) -LEAK_SCORE_BIAS: 4 +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 # Time parameters diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 43c0a9710..a90ca9155 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -35,17 +35,17 @@ - [`get_sync_committee`](#get_sync_committee) - [`get_base_reward`](#get_base_reward) - [`get_unslashed_participating_indices`](#get_unslashed_participating_indices) - - [`get_flag_deltas`](#get_flag_deltas) - - [New `get_inactivity_penalty_deltas`](#new-get_inactivity_penalty_deltas) + - [`get_flag_index_deltas`](#get_flag_index_deltas) + - [Modified `get_inactivity_penalty_deltas`](#modified-get_inactivity_penalty_deltas) - [Beacon state mutators](#beacon-state-mutators) - - [New `slash_validator`](#new-slash_validator) + - [Modified `slash_validator`](#modified-slash_validator) - [Block processing](#block-processing) - [Modified `process_attestation`](#modified-process_attestation) - [Modified `process_deposit`](#modified-process_deposit) - [Sync committee processing](#sync-committee-processing) - [Epoch processing](#epoch-processing) - [Justification and finalization](#justification-and-finalization) - - [Leak scores](#leak-scores) + - [Inactivity scores](#inactivity-scores) - [Rewards and penalties](#rewards-and-penalties) - [Slashings](#slashings) - [Participation flags updates](#participation-flags-updates) @@ -56,20 +56,17 @@ ## Introduction -Altair is a patch implementing the first hard fork to the beacon chain. -It has four main features: +Altair is the first beacon chain hard fork. Its main features are: -* Light client support via sync committees -* Incentive accounting reforms, reducing spec complexity - and [TODO] reducing the cost of processing chains that have very little or zero participation for a long span of epochs -* Update penalty configuration values, moving them toward their planned maximally punitive configuration -* Fork choice rule changes to address weaknesses recently discovered in the existing fork choice +* sync committees to support light clients +* incentive accounting reforms to reduce spec complexity +* penalty parameter updates to move them to their planned maximally punitive configuration ## Custom types | Name | SSZ equivalent | Description | | - | - | - | -| `ParticipationFlags` | `uint8` | A succinct representation of 8 boolean participation flags | +| `ParticipationFlags` | `uint8` | a succinct representation of 8 boolean participation flags | ## Constants @@ -90,8 +87,7 @@ It has four main features: | `TIMELY_TARGET_FLAG_NUMERATOR` | `32` | | `FLAG_DENOMINATOR` | `64` | -**Note**: The participatition flag fractions add up to 7/8. -The remaining 1/8 is for proposer incentives and other future micro-incentives. +**Note**: The sum of the participatition flag fractions (7/8) plus the proposer reward fraction (1/8) equals 1. ### Misc @@ -103,23 +99,23 @@ The remaining 1/8 is for proposer incentives and other future micro-incentives. ### Updated penalty values -This patch updates a few configuration values to move penalty constants toward their final, maxmium security values. +This patch updates a few configuration values to move penalty parameters toward their final, maxmium security values. *Note*: The spec does *not* override previous configuration values but instead creates new values and replaces usage throughout. | Name | Value | | - | - | | `ALTAIR_INACTIVITY_PENALTY_QUOTIENT` | `uint64(3 * 2**24)` (= 50,331,648) | -| `ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**6)` (=64) | +| `ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**6)` (= 64) | | `ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(2)` | ### Misc | Name | Value | -| - | - | +| - | - | | `SYNC_COMMITTEE_SIZE` | `uint64(2**10)` (= 1,024) | | `SYNC_SUBCOMMITTEE_SIZE` | `uint64(2**6)` (= 64) | -| `LEAK_SCORE_BIAS` | 4 | +| `INACTIVITY_SCORE_BIAS` | `uint64(4)` | ### Time parameters @@ -181,18 +177,18 @@ class BeaconState(Container): # Slashings slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances # Participation - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [New in Altair] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [New in Altair] + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [Modified in Altair] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [Modified in Altair] # Finality justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch previous_justified_checkpoint: Checkpoint current_justified_checkpoint: Checkpoint finalized_checkpoint: Checkpoint - # Light client sync committees + # Inactivity + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] # [New in Altair] + # Sync current_sync_committee: SyncCommittee # [New in Altair] next_sync_committee: SyncCommittee # [New in Altair] - # Leak - leak_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] # [New in Altair] ``` ### New containers @@ -307,7 +303,7 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: ```python def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epoch: Epoch) -> Set[ValidatorIndex]: """ - Retrieve the active and unslashed validator indices for the given epoch and flag index. + Return the active and unslashed validator indices for the given epoch and flag index. """ assert epoch in (get_previous_epoch(state), get_current_epoch(state)) if epoch == get_current_epoch(state): @@ -319,19 +315,17 @@ def get_unslashed_participating_indices(state: BeaconState, flag_index: int, epo return set(filter(lambda index: not state.validators[index].slashed, participating_indices)) ``` -#### `get_flag_deltas` +#### `get_flag_index_deltas` ```python -def get_flag_deltas(state: BeaconState, - flag_index: int, - numerator: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: +def get_flag_index_deltas(state: BeaconState, + flag_index: int, + numerator: uint64) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: """ - Compute the rewards and penalties associated with a particular duty, by scanning through the participation - flags to determine who participated and who did not and assigning them the appropriate rewards and penalties. + Return the deltas for a given flag index by scanning through the participation flags. """ rewards = [Gwei(0)] * len(state.validators) penalties = [Gwei(0)] * len(state.validators) - unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, get_previous_epoch(state)) increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balances to avoid uint64 overflow unslashed_participating_increments = get_total_balance(state, unslashed_participating_indices) // increment @@ -340,19 +334,17 @@ def get_flag_deltas(state: BeaconState, base_reward = get_base_reward(state, index) if index in unslashed_participating_indices: if is_in_inactivity_leak(state): - # Optimal participation is fully rewarded to cancel the inactivity penalty - rewards[index] = base_reward * numerator // FLAG_DENOMINATOR + # This flag reward cancels the inactivity penalty corresponding to the flag index + rewards[index] += Gwei(base_reward * numerator // FLAG_DENOMINATOR) else: - rewards[index] = ( - (base_reward * numerator * unslashed_participating_increments) - // (active_increments * FLAG_DENOMINATOR) - ) + reward_numerator = base_reward * numerator * unslashed_participating_increments + rewards[index] += Gwei(reward_numerator // (active_increments * FLAG_DENOMINATOR)) else: - penalties[index] = base_reward * numerator // FLAG_DENOMINATOR + penalties[index] += Gwei(base_reward * numerator // FLAG_DENOMINATOR) return rewards, penalties ``` -#### New `get_inactivity_penalty_deltas` +#### Modified `get_inactivity_penalty_deltas` *Note*: The function `get_inactivity_penalty_deltas` is modified in the selection of matching target indices and the removal of `BASE_REWARDS_PER_EPOCH`. @@ -360,39 +352,29 @@ and the removal of `BASE_REWARDS_PER_EPOCH`. ```python def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: """ - Compute the penalties associated with the inactivity leak, by scanning through the participation - flags to determine who participated and who did not, applying the leak penalty globally and applying - compensatory rewards to participants. + Return the inactivity penalty deltas by considering timely target participation flags and inactivity scores. """ rewards = [Gwei(0) for _ in range(len(state.validators))] penalties = [Gwei(0) for _ in range(len(state.validators))] - - if not is_in_inactivity_leak(state): - return rewards, penalties - - reward_numerator_sum = sum(numerator for (_, numerator) in get_flag_indices_and_numerators()) - matching_target_attesting_indices = get_unslashed_participating_indices( - state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state) - ) - for index in get_eligible_validator_indices(state): - # If validator is performing optimally this cancels all attestation rewards for a neutral balance - penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // FLAG_DENOMINATOR) - if index not in matching_target_attesting_indices and state.leak_scores[index] >= LEAK_SCORE_BIAS: - effective_balance = state.validators[index].effective_balance - leak_penalty = Gwei( - effective_balance * state.leak_scores[index] // LEAK_SCORE_BIAS // ALTAIR_INACTIVITY_PENALTY_QUOTIENT - ) - penalties[index] += leak_penalty - + 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 (_, numerator) in get_flag_indices_and_numerators(): + # This inactivity penalty cancels the flag reward corresponding to the flag index + penalties[index] += Gwei(get_base_reward(state, index) * numerator // FLAG_DENOMINATOR) + if index not in matching_target_indices: + penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] + penalty_denominator = INACTIVITY_SCORE_BIAS * ALTAIR_INACTIVITY_PENALTY_QUOTIENT + penalties[index] += Gwei(penalty_numerator // penalty_denominator) return rewards, penalties ``` ### Beacon state mutators -#### New `slash_validator` +#### Modified `slash_validator` -*Note*: The function `slash_validator` is modified -with the substitution of `MIN_SLASHING_PENALTY_QUOTIENT` with `ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT`. +*Note*: The function `slash_validator` is modified to use `ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT`. ```python def slash_validator(state: BeaconState, @@ -483,10 +465,9 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: increase_balance(state, get_beacon_proposer_index(state), proposer_reward) ``` - #### Modified `process_deposit` -*Note*: The function `process_deposit` is modified to initialize `leak_scores`, `previous_epoch_participation`, `current_epoch_participation`. +*Note*: The function `process_deposit` is modified to initialize `inactivity_scores`, `previous_epoch_participation`, `current_epoch_participation`. ```python def process_deposit(state: BeaconState, deposit: Deposit) -> None: @@ -504,7 +485,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: pubkey = deposit.data.pubkey amount = deposit.data.amount - validator_pubkeys = [v.pubkey for v in state.validators] + validator_pubkeys = [validator.pubkey for validator in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) which is not checked by the deposit contract deposit_message = DepositMessage( @@ -514,15 +495,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: ) domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks signing_root = compute_signing_root(deposit_message, domain) - if not bls.Verify(pubkey, signing_root, deposit.data.signature): - return - - # Add validator and balance entries - state.validators.append(get_validator_from_deposit(state, deposit)) - state.balances.append(amount) - state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair] - state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair] - state.leak_scores.append(0) # [New in Altair] + # Initialize validator if the deposit signature is valid + if bls.Verify(pubkey, signing_root, deposit.data.signature): + state.validators.append(get_validator_from_deposit(state, deposit)) + state.balances.append(amount) + state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) + state.inactivity_scores.append(0) else: # Increase balance by deposit amount index = ValidatorIndex(validator_pubkeys.index(pubkey)) @@ -563,7 +542,7 @@ def process_sync_committee(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_epoch(state: BeaconState) -> None: process_justification_and_finalization(state) # [Modified in Altair] - process_leak_updates(state) # [New in Altair] + process_inactivity_updates(state) # [New in Altair] process_rewards_and_penalties(state) # [Modified in Altair] process_registry_updates(state) process_slashings(state) # [Modified in Altair] @@ -622,36 +601,32 @@ def process_justification_and_finalization(state: BeaconState) -> None: state.finalized_checkpoint = old_current_justified_checkpoint ``` -#### Leak scores +#### Inactivity scores -*Note*: The function `process_leak_updates` is new. +*Note*: The function `process_inactivity_updates` is new. ```python -def process_leak_updates(state: BeaconState) -> None: - matching_target_attesting_indices = get_unslashed_participating_indices( - state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state) - ) +def process_inactivity_updates(state: BeaconState) -> None: for index in get_eligible_validator_indices(state): - if index in matching_target_attesting_indices: - if state.leak_scores[index] > 0: - state.leak_scores[index] -= 1 + 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.leak_scores[index] += LEAK_SCORE_BIAS + state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS ``` #### Rewards and penalties -*Note*: The function `process_rewards_and_penalties` is modified to support the incentive reforms. +*Note*: The function `process_rewards_and_penalties` is modified to support the incentive accounting reforms. ```python def process_rewards_and_penalties(state: BeaconState) -> None: # No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch if get_current_epoch(state) == GENESIS_EPOCH: return - flag_deltas = [ - get_flag_deltas(state, flag_index, flag_numerator) - for (flag_index, flag_numerator) in get_flag_indices_and_numerators() - ] + + flag_indices_and_numerators = get_flag_indices_and_numerators() + flag_deltas = [get_flag_index_deltas(state, index, numerator) for (index, numerator) in flag_indices_and_numerators] deltas = flag_deltas + [get_inactivity_penalty_deltas(state)] for (rewards, penalties) in deltas: for index in range(len(state.validators)): diff --git a/specs/altair/fork.md b/specs/altair/fork.md index 4d6c0c88a..b40464653 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -75,8 +75,8 @@ def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState: previous_justified_checkpoint=pre.previous_justified_checkpoint, current_justified_checkpoint=pre.current_justified_checkpoint, finalized_checkpoint=pre.finalized_checkpoint, - # Leak - leak_scores=[0 for _ in range(len(pre.validators))], + # Inactivity + inactivity_scores=[0 for _ in range(len(pre.validators))], ) # Fill in sync committees post.current_sync_committee = get_sync_committee(post, get_current_epoch(post)) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index a71e86a85..9fcd11852 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -257,7 +257,7 @@ The following values are (non-configurable) constants used throughout the specif | `WHISTLEBLOWER_REWARD_QUOTIENT` | `uint64(2**9)` (= 512) | | `PROPOSER_REWARD_QUOTIENT` | `uint64(2**3)` (= 8) | | `INACTIVITY_PENALTY_QUOTIENT` | `uint64(2**26)` (= 67,108,864) | -| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**7)` (=128) | +| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**7)` (= 128) | | `PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(1)` | - The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**13` epochs (about 36 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. Note this value will be upgraded to `2**24` after Phase 0 mainnet stabilizes to provide a faster recovery in the event of an inactivity leak. diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 14a5ecbba..281078d20 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -647,5 +647,5 @@ A validator client should be considered standalone and should consider the beaco 1) Private keys -- private keys should be protected from being exported accidentally or by an attacker. 2) Slashing -- before a validator client signs a message it should validate the data, check it against a local slashing database (do not sign a slashable attestation or block) and update its internal slashing database with the newly signed object. 3) Recovered validator -- Recovering a validator from a private key will result in an empty local slashing db. Best practice is to import (from a trusted source) that validator's attestation history. See [EIP 3076](https://github.com/ethereum/EIPs/pull/3076/files) for a standard slashing interchange format. -4) Far future signing requests -- A validator client can be requested to sign a far into the future attestation, resulting in a valid non-slashable request. If the validator client signs this message, it will result in it blocking itself from attesting any other attestation until the beacon-chain reaches that far into the future epoch. This will result in an inactivity leak and potential ejection due to low balance. +4) Far future signing requests -- A validator client can be requested to sign a far into the future attestation, resulting in a valid non-slashable request. If the validator client signs this message, it will result in it blocking itself from attesting any other attestation until the beacon-chain reaches that far into the future epoch. This will result in an inactivity penalty and potential ejection due to low balance. A validator client should prevent itself from signing such requests by: a) keeping a local time clock if possible and following best practices to stop time server attacks and b) refusing to sign, by default, any message that has a large (>6h) gap from the current slashing protection database indicated a time "jump" or a long offline event. The administrator can manually override this protection to restart the validator after a genuine long offline event. diff --git a/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py index 3689c0783..7bd2a91ba 100644 --- a/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py @@ -79,12 +79,12 @@ def test_empty_sync_committee_committee_genesis(spec, state): @with_all_phases_except([PHASE0, PHASE1]) @spec_state_test -def test_leak_scores(spec, state): +def test_inactivity_scores(spec, state): for _ in range(spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2): next_epoch_via_block(spec, state) assert spec.is_in_inactivity_leak(state) - previous_leak_scores = state.leak_scores.copy() + previous_inactivity_scores = state.inactivity_scores.copy() yield 'pre', state @@ -95,5 +95,5 @@ def test_leak_scores(spec, state): yield 'blocks', [signed_block] yield 'post', state - for pre, post in zip(previous_leak_scores, state.leak_scores): - assert post == pre + spec.LEAK_SCORE_BIAS + for pre, post in zip(previous_inactivity_scores, state.inactivity_scores): + assert post == pre + spec.INACTIVITY_SCORE_BIAS diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index ab4b9a282..8d34500b6 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -42,13 +42,13 @@ def run_deltas(spec, state): if is_post_altair(spec): def get_source_deltas(state): - return spec.get_flag_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_FLAG_NUMERATOR) + return spec.get_flag_index_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_FLAG_NUMERATOR) def get_head_deltas(state): - return spec.get_flag_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_FLAG_NUMERATOR) + return spec.get_flag_index_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_FLAG_NUMERATOR) def get_target_deltas(state): - return spec.get_flag_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_FLAG_NUMERATOR) + return spec.get_flag_index_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_FLAG_NUMERATOR) yield from run_attestation_component_deltas( spec, @@ -191,7 +191,6 @@ def run_get_inactivity_penalty_deltas(spec, state): matching_attesting_indices = spec.get_unslashed_participating_indices( state, spec.TIMELY_TARGET_FLAG_INDEX, spec.get_previous_epoch(state) ) - reward_numerator_sum = sum(numerator for (_, numerator) in spec.get_flag_indices_and_numerators()) eligible_indices = spec.get_eligible_validator_indices(state) for index in range(len(state.validators)): @@ -202,12 +201,15 @@ def run_get_inactivity_penalty_deltas(spec, state): if spec.is_in_inactivity_leak(state): # Compute base_penalty + base_reward = spec.get_base_reward(state, index) if not is_post_altair(spec): cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH - base_reward = spec.get_base_reward(state, index) base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index) else: - base_penalty = spec.get_base_reward(state, index) * reward_numerator_sum // spec.FLAG_DENOMINATOR + base_penalty = sum( + base_reward * numerator // spec.FLAG_DENOMINATOR + for (_, numerator) in spec.get_flag_indices_and_numerators() + ) if not has_enough_for_reward(spec, state, index): assert penalties[index] == 0 @@ -221,7 +223,7 @@ def run_get_inactivity_penalty_deltas(spec, state): def transition_state_to_leak(spec, state, epochs=None): if epochs is None: - # +1 to trigger leak_score transitions + # +1 to trigger inactivity_score transitions epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 1 assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY