Simplify justification and finalization accounting logic
Much of the simplification is cosmetic. The following changes are substantive: * Inactivity leak penalty specifically on missing the target, not both the target and the source * Even outside of quadratic leak scenarios, slashing victims suffer offline penalties
This commit is contained in:
parent
5fef8ea339
commit
d1d1b73fb1
|
@ -1883,10 +1883,11 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
|
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
|
||||||
return (
|
if epochs_since_finality <= 4:
|
||||||
get_base_reward(state, index) +
|
extra_penalty = 0
|
||||||
get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2
|
else:
|
||||||
)
|
extra_penalty = get_effective_balance(state, index) * min(epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2
|
||||||
|
return get_base_reward(state, index) + extra_penalty
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow.
|
Note: When applying penalties in the following balance recalculations implementers should make sure the `uint64` does not underflow.
|
||||||
|
@ -1896,22 +1897,8 @@ Note: When applying penalties in the following balance recalculations implemente
|
||||||
```python
|
```python
|
||||||
def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||||
epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch
|
epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch
|
||||||
if epochs_since_finality <= 4:
|
rewards = [0 for index in range(len(state.validator_registry))]
|
||||||
return compute_normal_justification_and_finalization_deltas(state)
|
penalties = [0 for index in range(len(state.validator_registry))]
|
||||||
else:
|
|
||||||
return compute_inactivity_leak_deltas(state)
|
|
||||||
```
|
|
||||||
|
|
||||||
When blocks are finalizing normally...
|
|
||||||
|
|
||||||
```python
|
|
||||||
def compute_normal_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
|
||||||
# deltas[0] for rewards
|
|
||||||
# deltas[1] for penalties
|
|
||||||
deltas = [
|
|
||||||
[0 for index in range(len(state.validator_registry))],
|
|
||||||
[0 for index in range(len(state.validator_registry))]
|
|
||||||
]
|
|
||||||
# Some helper variables
|
# Some helper variables
|
||||||
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
||||||
boundary_attesting_balance = get_attesting_balance(state, boundary_attestations)
|
boundary_attesting_balance = get_attesting_balance(state, boundary_attestations)
|
||||||
|
@ -1919,76 +1906,37 @@ def compute_normal_justification_and_finalization_deltas(state: BeaconState) ->
|
||||||
total_attesting_balance = get_attesting_balance(state, state.previous_epoch_attestations)
|
total_attesting_balance = get_attesting_balance(state, state.previous_epoch_attestations)
|
||||||
matching_head_attestations = get_previous_epoch_matching_head_attestations(state)
|
matching_head_attestations = get_previous_epoch_matching_head_attestations(state)
|
||||||
matching_head_balance = get_attesting_balance(state, matching_head_attestations)
|
matching_head_balance = get_attesting_balance(state, matching_head_attestations)
|
||||||
|
eligible_validators = [
|
||||||
|
i for i,v in enumerate(state.validator_registry) if is_active_validator(v, get_current_epoch(state)) or
|
||||||
|
(v.slashed and get_current_epoch(state) < v.withdrawable_epoch)
|
||||||
|
]
|
||||||
# Process rewards or penalties for all validators
|
# Process rewards or penalties for all validators
|
||||||
for index in get_active_validator_indices(state.validator_registry, get_previous_epoch(state)):
|
for index in eligible_validators:
|
||||||
# Expected FFG source
|
# Expected FFG source
|
||||||
if index in get_attesting_indices(state, state.previous_epoch_attestations):
|
if index in get_attesting_indices(state, state.previous_epoch_attestations):
|
||||||
deltas[0][index] += get_base_reward(state, index) * total_attesting_balance // total_balance
|
rewards[index] += get_base_reward(state, index) * total_attesting_balance // total_balance
|
||||||
# Inclusion speed bonus
|
# Inclusion speed bonus
|
||||||
deltas[0][index] += (
|
rewards[index] += (
|
||||||
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
|
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
|
||||||
inclusion_distance(state, index)
|
inclusion_distance(state, index)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
deltas[1][index] += get_base_reward(state, index)
|
penalties[index] += get_base_reward(state, index)
|
||||||
# Expected FFG target
|
# Expected FFG target
|
||||||
if index in get_attesting_indices(state, boundary_attestations):
|
if index in get_attesting_indices(state, boundary_attestations):
|
||||||
deltas[0][index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance
|
rewards[index] += get_base_reward(state, index) * boundary_attesting_balance // total_balance
|
||||||
else:
|
else:
|
||||||
deltas[1][index] += get_base_reward(state, index)
|
penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality)
|
||||||
# Expected head
|
# Expected head
|
||||||
if index in get_attesting_indices(state, matching_head_attestations):
|
if index in get_attesting_indices(state, matching_head_attestations):
|
||||||
deltas[0][index] += get_base_reward(state, index) * matching_head_balance // total_balance
|
rewards[index] += get_base_reward(state, index) * matching_head_balance // total_balance
|
||||||
else:
|
else:
|
||||||
deltas[1][index] += get_base_reward(state, index)
|
penalties[index] += get_base_reward(state, index)
|
||||||
# Proposer bonus
|
# Proposer bonus
|
||||||
if index in get_attesting_indices(state, state.previous_epoch_attestations):
|
if index in get_attesting_indices(state, state.previous_epoch_attestations):
|
||||||
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
|
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
|
||||||
deltas[0][proposer_index] += get_base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
rewards[proposer_index] += get_base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
||||||
return deltas
|
return [rewards, penalties]
|
||||||
```
|
|
||||||
|
|
||||||
When blocks are not finalizing normally...
|
|
||||||
|
|
||||||
```python
|
|
||||||
def compute_inactivity_leak_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
|
||||||
# deltas[0] for rewards
|
|
||||||
# deltas[1] for penalties
|
|
||||||
deltas = [
|
|
||||||
[0 for index in range(len(state.validator_registry))],
|
|
||||||
[0 for index in range(len(state.validator_registry))]
|
|
||||||
]
|
|
||||||
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
|
||||||
matching_head_attestations = get_previous_epoch_matching_head_attestations(state)
|
|
||||||
active_validator_indices = get_active_validator_indices(state.validator_registry, get_previous_epoch(state))
|
|
||||||
epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch
|
|
||||||
for index in active_validator_indices:
|
|
||||||
if index not in get_attesting_indices(state, state.previous_epoch_attestations):
|
|
||||||
deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality)
|
|
||||||
else:
|
|
||||||
# If a validator did attest, apply a small penalty for getting attestations included late
|
|
||||||
deltas[0][index] += (
|
|
||||||
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
|
|
||||||
inclusion_distance(state, index)
|
|
||||||
)
|
|
||||||
deltas[1][index] += get_base_reward(state, index)
|
|
||||||
if index not in get_attesting_indices(state, boundary_attestations):
|
|
||||||
deltas[1][index] += get_inactivity_penalty(state, index, epochs_since_finality)
|
|
||||||
if index not in get_attesting_indices(state, matching_head_attestations):
|
|
||||||
deltas[1][index] += get_base_reward(state, index)
|
|
||||||
# Penalize slashed-but-inactive validators as though they were active but offline
|
|
||||||
for index in range(len(state.validator_registry)):
|
|
||||||
eligible = (
|
|
||||||
index not in active_validator_indices and
|
|
||||||
state.validator_registry[index].slashed and
|
|
||||||
get_current_epoch(state) < state.validator_registry[index].withdrawable_epoch
|
|
||||||
)
|
|
||||||
if eligible:
|
|
||||||
deltas[1][index] += (
|
|
||||||
2 * get_inactivity_penalty(state, index, epochs_since_finality) +
|
|
||||||
get_base_reward(state, index)
|
|
||||||
)
|
|
||||||
return deltas
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Crosslinks
|
##### Crosslinks
|
||||||
|
|
Loading…
Reference in New Issue