Skip to content

Commit

Permalink
Simplify justification and finalization accounting logic
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vbuterin committed Mar 21, 2019
1 parent 5fef8ea commit d1d1b73
Showing 1 changed file with 21 additions and 73 deletions.
94 changes: 21 additions & 73 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -1883,10 +1883,11 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:

```python
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
return (
get_base_reward(state, index) +
get_effective_balance(state, index) * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2
)
if epochs_since_finality <= 4:
extra_penalty = 0
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.
Expand All @@ -1896,99 +1897,46 @@ Note: When applying penalties in the following balance recalculations implemente
```python
def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
epochs_since_finality = get_current_epoch(state) + 1 - state.finalized_epoch
if epochs_since_finality <= 4:
return compute_normal_justification_and_finalization_deltas(state)
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))]
]
rewards = [0 for index in range(len(state.validator_registry))]
penalties = [0 for index in range(len(state.validator_registry))]
# Some helper variables
boundary_attestations = get_previous_epoch_boundary_attestations(state)
boundary_attesting_balance = get_attesting_balance(state, boundary_attestations)
total_balance = get_previous_total_balance(state)
total_attesting_balance = get_attesting_balance(state, state.previous_epoch_attestations)
matching_head_attestations = get_previous_epoch_matching_head_attestations(state)
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
for index in get_active_validator_indices(state.validator_registry, get_previous_epoch(state)):
for index in eligible_validators:
# Expected FFG source
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
deltas[0][index] += (
rewards[index] += (
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
inclusion_distance(state, index)
)
else:
deltas[1][index] += get_base_reward(state, index)
penalties[index] += get_base_reward(state, index)
# Expected FFG target
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:
deltas[1][index] += get_base_reward(state, index)
penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality)
# Expected head
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:
deltas[1][index] += get_base_reward(state, index)
penalties[index] += get_base_reward(state, index)
# Proposer bonus
if index in get_attesting_indices(state, state.previous_epoch_attestations):
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
deltas[0][proposer_index] += get_base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
return deltas
```

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
rewards[proposer_index] += get_base_reward(state, index) // ATTESTATION_INCLUSION_REWARD_QUOTIENT
return [rewards, penalties]
```

##### Crosslinks
Expand Down

0 comments on commit d1d1b73

Please sign in to comment.