Merge pull request #2395 from ethereum/vbuterin-patch-11
Update inactivity penalty deltas processing
This commit is contained in:
commit
ebafc943c6
|
@ -22,6 +22,8 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 512
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# 2**2 (= 4)
|
# 2**2 (= 4)
|
||||||
INACTIVITY_SCORE_BIAS: 4
|
INACTIVITY_SCORE_BIAS: 4
|
||||||
|
# 2**4 (= 16)
|
||||||
|
INACTIVITY_SCORE_RECOVERY_RATE: 16
|
||||||
|
|
||||||
|
|
||||||
# Signature domains
|
# Signature domains
|
||||||
|
|
|
@ -22,6 +22,8 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# 2**2 (= 4)
|
# 2**2 (= 4)
|
||||||
INACTIVITY_SCORE_BIAS: 4
|
INACTIVITY_SCORE_BIAS: 4
|
||||||
|
# 2**4 (= 16)
|
||||||
|
INACTIVITY_SCORE_RECOVERY_RATE: 16
|
||||||
|
|
||||||
|
|
||||||
# Signature domains
|
# Signature domains
|
||||||
|
|
|
@ -127,6 +127,7 @@ This patch updates a few configuration values to move penalty parameters closer
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
| - | - |
|
| - | - |
|
||||||
| `INACTIVITY_SCORE_BIAS` | `uint64(4)` |
|
| `INACTIVITY_SCORE_BIAS` | `uint64(4)` |
|
||||||
|
| `INACTIVITY_SCORE_RECOVERY_RATE` | `uint64(16)` |
|
||||||
|
|
||||||
### Domain types
|
### Domain types
|
||||||
|
|
||||||
|
@ -417,14 +418,13 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
|
||||||
"""
|
"""
|
||||||
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
penalties = [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)
|
||||||
previous_epoch = get_previous_epoch(state)
|
matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch)
|
||||||
matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch)
|
for index in get_eligible_validator_indices(state):
|
||||||
for index in get_eligible_validator_indices(state):
|
if index not in matching_target_indices:
|
||||||
if index not in matching_target_indices:
|
penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index]
|
||||||
penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index]
|
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
|
||||||
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
|
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
|
||||||
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
|
|
||||||
return rewards, penalties
|
return rewards, penalties
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -624,12 +624,19 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_inactivity_updates(state: BeaconState) -> None:
|
def process_inactivity_updates(state: BeaconState) -> None:
|
||||||
|
# Score updates based on previous epoch participation, skip genesis epoch
|
||||||
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||||
|
return
|
||||||
|
|
||||||
for index in get_eligible_validator_indices(state):
|
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 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] -= min(1, state.inactivity_scores[index])
|
||||||
state.inactivity_scores[index] -= 1
|
else:
|
||||||
elif is_in_inactivity_leak(state):
|
|
||||||
state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS
|
state.inactivity_scores[index] += INACTIVITY_SCORE_BIAS
|
||||||
|
# Decrease the score of all validators for forgiveness when not during a leak
|
||||||
|
if not is_in_inactivity_leak(state):
|
||||||
|
state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE, state.inactivity_scores[index])
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Rewards and penalties
|
#### Rewards and penalties
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
|
from eth2spec.test.context import spec_state_test, with_altair_and_later
|
||||||
|
from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores
|
||||||
|
from eth2spec.test.helpers.state import (
|
||||||
|
next_epoch_via_block,
|
||||||
|
)
|
||||||
|
from eth2spec.test.helpers.epoch_processing import (
|
||||||
|
run_epoch_processing_with
|
||||||
|
)
|
||||||
|
from eth2spec.test.helpers.random import (
|
||||||
|
randomize_attestation_participation,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_full_participation(spec, state):
|
||||||
|
full_flags = spec.ParticipationFlags(0)
|
||||||
|
for flag_index in range(len(spec.PARTICIPATION_FLAG_WEIGHTS)):
|
||||||
|
full_flags = spec.add_flag(full_flags, flag_index)
|
||||||
|
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
state.current_epoch_participation[index] = full_flags
|
||||||
|
state.previous_epoch_participation[index] = full_flags
|
||||||
|
|
||||||
|
|
||||||
|
def run_process_inactivity_updates(spec, state):
|
||||||
|
yield from run_epoch_processing_with(spec, state, 'process_inactivity_updates')
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_genesis(spec, state):
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Genesis epoch processing is skipped
|
||||||
|
# Thus all of following tests all go past genesis epoch to test core functionality
|
||||||
|
#
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_all_zero_inactivity_scores_empty_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
state.inactivity_scores = [0] * len(state.validators)
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_all_zero_inactivity_scores_random_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
state.inactivity_scores = [0] * len(state.validators)
|
||||||
|
randomize_attestation_participation(spec, state, rng=Random(5555))
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_all_zero_inactivity_scores_full_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
set_full_participation(spec, state)
|
||||||
|
state.inactivity_scores = [0] * len(state.validators)
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_inactivity_scores_empty_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(9999))
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_inactivity_scores_random_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
randomize_attestation_participation(spec, state, rng=Random(22222))
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(22222))
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_inactivity_scores_full_participation(spec, state):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
set_full_participation(spec, state)
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(33333))
|
||||||
|
yield from run_process_inactivity_updates(spec, state)
|
|
@ -0,0 +1,118 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
|
from eth2spec.test.context import (
|
||||||
|
with_altair_and_later,
|
||||||
|
spec_test,
|
||||||
|
spec_state_test,
|
||||||
|
with_custom_state,
|
||||||
|
single_phase,
|
||||||
|
low_balances, misc_balances,
|
||||||
|
)
|
||||||
|
from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores
|
||||||
|
from eth2spec.test.helpers.rewards import leaking
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_inactivity_scores_0(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(9999))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9999))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_inactivity_scores_1(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(10000))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10000))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_zero_half_random_inactivity_scores(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(10101))
|
||||||
|
half_val_point = len(state.validators) // 2
|
||||||
|
state.inactivity_scores = [0] * half_val_point + state.inactivity_scores[half_val_point:]
|
||||||
|
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10101))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_random_high_inactivity_scores(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_random_inactivity_scores_low_balances_0(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(11111))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_random_inactivity_scores_low_balances_1(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(22222))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_full_random_misc_balances(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(33333))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Leaking variants
|
||||||
|
#
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
@leaking()
|
||||||
|
def test_random_inactivity_scores_leaking_0(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(9999))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9999))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
@leaking()
|
||||||
|
def test_random_inactivity_scores_leaking_1(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(10000))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10000))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
@leaking()
|
||||||
|
def test_half_zero_half_random_inactivity_scores_leaking(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, rng=Random(10101))
|
||||||
|
half_val_point = len(state.validators) // 2
|
||||||
|
state.inactivity_scores = [0] * half_val_point + state.inactivity_scores[half_val_point:]
|
||||||
|
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(10101))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
@leaking()
|
||||||
|
def test_random_high_inactivity_scores_leaking(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998))
|
||||||
|
|
||||||
|
|
||||||
|
@with_altair_and_later
|
||||||
|
@spec_state_test
|
||||||
|
@leaking(epochs=5)
|
||||||
|
def test_random_high_inactivity_scores_leaking_5_epochs(spec, state):
|
||||||
|
randomize_inactivity_scores(spec, state, minimum=500000, maximum=5000000, rng=Random(9998))
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(9998))
|
|
@ -9,6 +9,7 @@ def get_process_calls(spec):
|
||||||
# or the old function will stick around.
|
# or the old function will stick around.
|
||||||
return [
|
return [
|
||||||
'process_justification_and_finalization',
|
'process_justification_and_finalization',
|
||||||
|
'process_inactivity_updates', # altair
|
||||||
'process_rewards_and_penalties',
|
'process_rewards_and_penalties',
|
||||||
'process_registry_updates',
|
'process_registry_updates',
|
||||||
'process_reveal_deadlines', # custody game
|
'process_reveal_deadlines', # custody game
|
||||||
|
@ -26,7 +27,7 @@ def get_process_calls(spec):
|
||||||
'process_participation_flag_updates' if is_post_altair(spec) else (
|
'process_participation_flag_updates' if is_post_altair(spec) else (
|
||||||
'process_participation_record_updates'
|
'process_participation_record_updates'
|
||||||
),
|
),
|
||||||
'process_sync_committee_updates',
|
'process_sync_committee_updates', # altair
|
||||||
'process_shard_epoch_increment' # sharding
|
'process_shard_epoch_increment' # sharding
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
|
|
||||||
|
def randomize_inactivity_scores(spec, state, minimum=0, maximum=50000, rng=Random(4242)):
|
||||||
|
state.inactivity_scores = [rng.randint(minimum, maximum) for _ in range(len(state.validators))]
|
|
@ -272,7 +272,6 @@ _cache_dict = LRU(size=10)
|
||||||
|
|
||||||
|
|
||||||
def leaking(epochs=None):
|
def leaking(epochs=None):
|
||||||
|
|
||||||
def deco(fn):
|
def deco(fn):
|
||||||
def entry(*args, spec, state, **kw):
|
def entry(*args, spec, state, **kw):
|
||||||
# If the pre-state is not already known in the LRU, then take it,
|
# If the pre-state is not already known in the LRU, then take it,
|
||||||
|
|
|
@ -39,8 +39,16 @@ def test_full_random_3(spec, state):
|
||||||
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
@spec_test
|
@spec_test
|
||||||
@single_phase
|
@single_phase
|
||||||
def test_full_random_low_balances(spec, state):
|
def test_full_random_low_balances_0(spec, state):
|
||||||
yield from rewards_helpers.run_test_full_random(spec, state)
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(5050))
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_full_random_low_balances_1(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(6060))
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
@ -48,4 +56,4 @@ def test_full_random_low_balances(spec, state):
|
||||||
@spec_test
|
@spec_test
|
||||||
@single_phase
|
@single_phase
|
||||||
def test_full_random_misc_balances(spec, state):
|
def test_full_random_misc_balances(spec, state):
|
||||||
yield from rewards_helpers.run_test_full_random(spec, state)
|
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(7070))
|
||||||
|
|
|
@ -32,9 +32,8 @@ The provided pre-state is already transitioned to just before the specific sub-t
|
||||||
|
|
||||||
Sub-transitions:
|
Sub-transitions:
|
||||||
|
|
||||||
Sub-transitions:
|
|
||||||
|
|
||||||
- `justification_and_finalization`
|
- `justification_and_finalization`
|
||||||
|
- `inactivity_penalty_updates`
|
||||||
- `rewards_and_penalties`
|
- `rewards_and_penalties`
|
||||||
- `registry_updates`
|
- `registry_updates`
|
||||||
- `slashings`
|
- `slashings`
|
||||||
|
@ -44,5 +43,6 @@ Sub-transitions:
|
||||||
- `randao_mixes_reset`
|
- `randao_mixes_reset`
|
||||||
- `historical_roots_update`
|
- `historical_roots_update`
|
||||||
- `participation_record_updates`
|
- `participation_record_updates`
|
||||||
|
- `sync_committee_updates`
|
||||||
|
|
||||||
The resulting state should match the expected `post` state.
|
The resulting state should match the expected `post` state.
|
||||||
|
|
Loading…
Reference in New Issue