Merge pull request #2198 from ethereum/hf1-config
update penalty config values for hf1
This commit is contained in:
commit
e35b850181
|
@ -2,6 +2,16 @@
|
||||||
|
|
||||||
CONFIG_NAME: "mainnet"
|
CONFIG_NAME: "mainnet"
|
||||||
|
|
||||||
|
# Updated penalty values
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
# 3 * 2**24) (= 50,331,648)
|
||||||
|
HF1_INACTIVITY_PENALTY_QUOTIENT: 50331648
|
||||||
|
# 2**6 (= 64)
|
||||||
|
HF1_MIN_SLASHING_PENALTY_QUOTIENT: 64
|
||||||
|
# 2
|
||||||
|
HF1_PROPORTIONAL_SLASHING_MULTIPLIER: 2
|
||||||
|
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# 2**10 (=1,024)
|
# 2**10 (=1,024)
|
||||||
|
|
|
@ -2,6 +2,16 @@
|
||||||
|
|
||||||
CONFIG_NAME: "minimal"
|
CONFIG_NAME: "minimal"
|
||||||
|
|
||||||
|
# Updated penalty values
|
||||||
|
# ---------------------------------------------------------------
|
||||||
|
# 3 * 2**24) (= 50,331,648)
|
||||||
|
HF1_INACTIVITY_PENALTY_QUOTIENT: 50331648
|
||||||
|
# 2**6 (= 64)
|
||||||
|
HF1_MIN_SLASHING_PENALTY_QUOTIENT: 64
|
||||||
|
# 2
|
||||||
|
HF1_PROPORTIONAL_SLASHING_MULTIPLIER: 2
|
||||||
|
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# [customized]
|
# [customized]
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
- [Participation rewards](#participation-rewards)
|
- [Participation rewards](#participation-rewards)
|
||||||
- [Misc](#misc)
|
- [Misc](#misc)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
|
- [Updated penalty values](#updated-penalty-values)
|
||||||
- [Misc](#misc-1)
|
- [Misc](#misc-1)
|
||||||
- [Time parameters](#time-parameters)
|
- [Time parameters](#time-parameters)
|
||||||
- [Domain types](#domain-types)
|
- [Domain types](#domain-types)
|
||||||
|
@ -34,6 +35,8 @@
|
||||||
- [`get_unslashed_participating_indices`](#get_unslashed_participating_indices)
|
- [`get_unslashed_participating_indices`](#get_unslashed_participating_indices)
|
||||||
- [`get_flag_deltas`](#get_flag_deltas)
|
- [`get_flag_deltas`](#get_flag_deltas)
|
||||||
- [New `get_inactivity_penalty_deltas`](#new-get_inactivity_penalty_deltas)
|
- [New `get_inactivity_penalty_deltas`](#new-get_inactivity_penalty_deltas)
|
||||||
|
- [Beacon state mutators](#beacon-state-mutators)
|
||||||
|
- [New `slash_validator`](#new-slash_validator)
|
||||||
- [Block processing](#block-processing)
|
- [Block processing](#block-processing)
|
||||||
- [New `process_attestation`](#new-process_attestation)
|
- [New `process_attestation`](#new-process_attestation)
|
||||||
- [New `process_deposit`](#new-process_deposit)
|
- [New `process_deposit`](#new-process_deposit)
|
||||||
|
@ -41,6 +44,7 @@
|
||||||
- [Epoch processing](#epoch-processing)
|
- [Epoch processing](#epoch-processing)
|
||||||
- [New `process_justification_and_finalization`](#new-process_justification_and_finalization)
|
- [New `process_justification_and_finalization`](#new-process_justification_and_finalization)
|
||||||
- [New `process_rewards_and_penalties`](#new-process_rewards_and_penalties)
|
- [New `process_rewards_and_penalties`](#new-process_rewards_and_penalties)
|
||||||
|
- [New `process_slashings`](#new-process_slashings)
|
||||||
- [Sync committee updates](#sync-committee-updates)
|
- [Sync committee updates](#sync-committee-updates)
|
||||||
- [Participation flags updates](#participation-flags-updates)
|
- [Participation flags updates](#participation-flags-updates)
|
||||||
|
|
||||||
|
@ -49,11 +53,13 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This is a patch implementing the first hard fork to the beacon chain, tentatively named HF1 pending a permanent name. It has three main features:
|
This is a patch implementing the first hard fork to the beacon chain, tentatively named HF1 pending a permanent name.
|
||||||
|
It has four main features:
|
||||||
|
|
||||||
* Light client support via sync committees
|
* Light client support via sync committees
|
||||||
* Incentive accounting reforms, reducing spec complexity
|
* 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
|
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
|
* Fork choice rule changes to address weaknesses recently discovered in the existing fork choice
|
||||||
|
|
||||||
## Custom types
|
## Custom types
|
||||||
|
@ -97,6 +103,18 @@ The reward fractions add up to 7/8, leaving the remaining 1/8 for proposer rewar
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
### Updated penalty values
|
||||||
|
|
||||||
|
This patch updates a few configuration values to move penalty constants 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 |
|
||||||
|
| - | - |
|
||||||
|
| `HF1_INACTIVITY_PENALTY_QUOTIENT` | `uint64(3 * 2**24)` (= 50,331,648) |
|
||||||
|
| `HF1_MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**6)` (=64) |
|
||||||
|
| `HF1_PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(2)` |
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
|
@ -328,7 +346,8 @@ def get_flag_deltas(state: BeaconState,
|
||||||
|
|
||||||
#### New `get_inactivity_penalty_deltas`
|
#### New `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`.
|
*Note*: The function `get_inactivity_penalty_deltas` is modified in the selection of matching target indices
|
||||||
|
and the removal of `BASE_REWARDS_PER_EPOCH`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
@ -348,12 +367,47 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
|
||||||
penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // REWARD_DENOMINATOR)
|
penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // REWARD_DENOMINATOR)
|
||||||
if index not in matching_target_attesting_indices:
|
if index not in matching_target_attesting_indices:
|
||||||
effective_balance = state.validators[index].effective_balance
|
effective_balance = state.validators[index].effective_balance
|
||||||
penalties[index] += Gwei(effective_balance * get_finality_delay(state) // INACTIVITY_PENALTY_QUOTIENT)
|
penalties[index] += Gwei(
|
||||||
|
effective_balance * get_finality_delay(state)
|
||||||
|
// HF1_INACTIVITY_PENALTY_QUOTIENT
|
||||||
|
)
|
||||||
|
|
||||||
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
return rewards, penalties
|
return rewards, penalties
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Beacon state mutators
|
||||||
|
|
||||||
|
#### New `slash_validator`
|
||||||
|
|
||||||
|
*Note*: The function `slash_validator` is modified
|
||||||
|
with the substitution of `MIN_SLASHING_PENALTY_QUOTIENT` with `HF1_MIN_SLASHING_PENALTY_QUOTIENT`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def slash_validator(state: BeaconState,
|
||||||
|
slashed_index: ValidatorIndex,
|
||||||
|
whistleblower_index: ValidatorIndex=None) -> None:
|
||||||
|
"""
|
||||||
|
Slash the validator with index ``slashed_index``.
|
||||||
|
"""
|
||||||
|
epoch = get_current_epoch(state)
|
||||||
|
initiate_validator_exit(state, slashed_index)
|
||||||
|
validator = state.validators[slashed_index]
|
||||||
|
validator.slashed = True
|
||||||
|
validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
|
||||||
|
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
||||||
|
decrease_balance(state, slashed_index, validator.effective_balance // HF1_MIN_SLASHING_PENALTY_QUOTIENT)
|
||||||
|
|
||||||
|
# Apply proposer and whistleblower rewards
|
||||||
|
proposer_index = get_beacon_proposer_index(state)
|
||||||
|
if whistleblower_index is None:
|
||||||
|
whistleblower_index = proposer_index
|
||||||
|
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
||||||
|
proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
|
||||||
|
increase_balance(state, proposer_index, proposer_reward)
|
||||||
|
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
||||||
|
```
|
||||||
|
|
||||||
### Block processing
|
### Block processing
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -576,6 +630,24 @@ def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||||
decrease_balance(state, ValidatorIndex(index), penalties[index])
|
decrease_balance(state, ValidatorIndex(index), penalties[index])
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### New `process_slashings`
|
||||||
|
|
||||||
|
*Note*: The function `process_slashings` is modified
|
||||||
|
with the substitution of `PROPORTIONAL_SLASHING_MULTIPLIER` with `HF1_PROPORTIONAL_SLASHING_MULTIPLIER`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def process_slashings(state: BeaconState) -> None:
|
||||||
|
epoch = get_current_epoch(state)
|
||||||
|
total_balance = get_total_active_balance(state)
|
||||||
|
adjusted_total_slashing_balance = min(sum(state.slashings) * HF1_PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
|
||||||
|
for index, validator in enumerate(state.validators):
|
||||||
|
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||||
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||||
|
penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||||
|
penalty = penalty_numerator // total_balance * increment
|
||||||
|
decrease_balance(state, ValidatorIndex(index), penalty)
|
||||||
|
```
|
||||||
|
|
||||||
#### Sync committee updates
|
#### Sync committee updates
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
|
from eth2spec.test.context import is_post_lightclient_patch
|
||||||
from eth2spec.test.helpers.block_header import sign_block_header
|
from eth2spec.test.helpers.block_header import sign_block_header
|
||||||
from eth2spec.test.helpers.keys import pubkey_to_privkey
|
from eth2spec.test.helpers.keys import pubkey_to_privkey
|
||||||
from eth2spec.test.helpers.state import get_balance
|
from eth2spec.test.helpers.state import get_balance
|
||||||
|
|
||||||
|
|
||||||
|
def get_min_slashing_penalty_quotient(spec):
|
||||||
|
if is_post_lightclient_patch(spec):
|
||||||
|
return spec.HF1_MIN_SLASHING_PENALTY_QUOTIENT
|
||||||
|
else:
|
||||||
|
return spec.MIN_SLASHING_PENALTY_QUOTIENT
|
||||||
|
|
||||||
|
|
||||||
def check_proposer_slashing_effect(spec, pre_state, state, slashed_index):
|
def check_proposer_slashing_effect(spec, pre_state, state, slashed_index):
|
||||||
slashed_validator = state.validators[slashed_index]
|
slashed_validator = state.validators[slashed_index]
|
||||||
assert slashed_validator.slashed
|
assert slashed_validator.slashed
|
||||||
|
@ -10,7 +18,7 @@ def check_proposer_slashing_effect(spec, pre_state, state, slashed_index):
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
proposer_index = spec.get_beacon_proposer_index(state)
|
proposer_index = spec.get_beacon_proposer_index(state)
|
||||||
slash_penalty = state.validators[slashed_index].effective_balance // spec.MIN_SLASHING_PENALTY_QUOTIENT
|
slash_penalty = state.validators[slashed_index].effective_balance // get_min_slashing_penalty_quotient(spec)
|
||||||
whistleblower_reward = state.validators[slashed_index].effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
|
whistleblower_reward = state.validators[slashed_index].effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
|
||||||
if proposer_index != slashed_index:
|
if proposer_index != slashed_index:
|
||||||
# slashed validator lost initial slash penalty
|
# slashed validator lost initial slash penalty
|
||||||
|
|
|
@ -4,6 +4,7 @@ from eth2spec.test.context import (
|
||||||
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
||||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
||||||
get_indexed_attestation_participants, get_attestation_2_data, get_attestation_1_data
|
get_indexed_attestation_participants, get_attestation_2_data, get_attestation_1_data
|
||||||
|
from eth2spec.test.helpers.proposer_slashings import get_min_slashing_penalty_quotient
|
||||||
from eth2spec.test.helpers.state import (
|
from eth2spec.test.helpers.state import (
|
||||||
get_balance,
|
get_balance,
|
||||||
next_epoch_via_block,
|
next_epoch_via_block,
|
||||||
|
@ -70,7 +71,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
|
||||||
expected_balance = (
|
expected_balance = (
|
||||||
pre_proposer_balance
|
pre_proposer_balance
|
||||||
+ total_proposer_rewards
|
+ total_proposer_rewards
|
||||||
- pre_slashings[proposer_index] // spec.MIN_SLASHING_PENALTY_QUOTIENT
|
- pre_slashings[proposer_index] // get_min_slashing_penalty_quotient(spec)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert get_balance(state, proposer_index) == expected_balance
|
assert get_balance(state, proposer_index) == expected_balance
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_lightclient_patch
|
||||||
from eth2spec.test.helpers.epoch_processing import (
|
from eth2spec.test.helpers.epoch_processing import (
|
||||||
run_epoch_processing_with, run_epoch_processing_to
|
run_epoch_processing_with, run_epoch_processing_to
|
||||||
)
|
)
|
||||||
|
@ -23,12 +23,19 @@ def slash_validators(spec, state, indices, out_epochs):
|
||||||
] = total_slashed_balance
|
] = total_slashed_balance
|
||||||
|
|
||||||
|
|
||||||
|
def get_slashing_multiplier(spec):
|
||||||
|
if is_post_lightclient_patch(spec):
|
||||||
|
return spec.HF1_PROPORTIONAL_SLASHING_MULTIPLIER
|
||||||
|
else:
|
||||||
|
return spec.PROPORTIONAL_SLASHING_MULTIPLIER
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_max_penalties(spec, state):
|
def test_max_penalties(spec, state):
|
||||||
# Slashed count to ensure that enough validators are slashed to induce maximum penalties
|
# Slashed count to ensure that enough validators are slashed to induce maximum penalties
|
||||||
slashed_count = min(
|
slashed_count = min(
|
||||||
(len(state.validators) // spec.PROPORTIONAL_SLASHING_MULTIPLIER) + 1,
|
(len(state.validators) // get_slashing_multiplier(spec)) + 1,
|
||||||
# Can't slash more than validator count!
|
# Can't slash more than validator count!
|
||||||
len(state.validators)
|
len(state.validators)
|
||||||
)
|
)
|
||||||
|
@ -40,7 +47,7 @@ def test_max_penalties(spec, state):
|
||||||
total_balance = spec.get_total_active_balance(state)
|
total_balance = spec.get_total_active_balance(state)
|
||||||
total_penalties = sum(state.slashings)
|
total_penalties = sum(state.slashings)
|
||||||
|
|
||||||
assert total_balance // spec.PROPORTIONAL_SLASHING_MULTIPLIER <= total_penalties
|
assert total_balance // get_slashing_multiplier(spec) <= total_penalties
|
||||||
|
|
||||||
yield from run_process_slashings(spec, state)
|
yield from run_process_slashings(spec, state)
|
||||||
|
|
||||||
|
@ -50,7 +57,30 @@ def test_max_penalties(spec, state):
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_small_penalty(spec, state):
|
def test_low_penalty(spec, state):
|
||||||
|
# Slashed count is one tenth of validator set
|
||||||
|
slashed_count = (len(state.validators) // 10) + 1
|
||||||
|
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2)
|
||||||
|
|
||||||
|
slashed_indices = list(range(slashed_count))
|
||||||
|
slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count)
|
||||||
|
|
||||||
|
pre_state = state.copy()
|
||||||
|
|
||||||
|
yield from run_process_slashings(spec, state)
|
||||||
|
|
||||||
|
for i in slashed_indices:
|
||||||
|
assert 0 < state.balances[i] < pre_state.balances[i]
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_minimal_penalty(spec, state):
|
||||||
|
#
|
||||||
|
# When very few slashings, the resulting slashing penalty gets rounded down
|
||||||
|
# to zero so the result of `process_slashings` is null
|
||||||
|
#
|
||||||
|
|
||||||
# Just the bare minimum for this one validator
|
# Just the bare minimum for this one validator
|
||||||
state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE
|
state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE
|
||||||
# All the other validators get the maximum.
|
# All the other validators get the maximum.
|
||||||
|
@ -74,11 +104,13 @@ def test_small_penalty(spec, state):
|
||||||
|
|
||||||
expected_penalty = (
|
expected_penalty = (
|
||||||
state.validators[0].effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
|
state.validators[0].effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
* (3 * total_penalties)
|
* (get_slashing_multiplier(spec) * total_penalties)
|
||||||
// total_balance
|
// total_balance
|
||||||
* spec.EFFECTIVE_BALANCE_INCREMENT
|
* spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
)
|
)
|
||||||
assert state.balances[0] == pre_slash_balances[0] - expected_penalty
|
|
||||||
|
assert expected_penalty == 0
|
||||||
|
assert state.balances[0] == pre_slash_balances[0]
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
@ -96,7 +128,7 @@ def test_scaled_penalties(spec, state):
|
||||||
state.slashings[5] = base + (incr * 6)
|
state.slashings[5] = base + (incr * 6)
|
||||||
state.slashings[spec.EPOCHS_PER_SLASHINGS_VECTOR - 1] = base + (incr * 7)
|
state.slashings[spec.EPOCHS_PER_SLASHINGS_VECTOR - 1] = base + (incr * 7)
|
||||||
|
|
||||||
slashed_count = len(state.validators) // (spec.PROPORTIONAL_SLASHING_MULTIPLIER + 1)
|
slashed_count = len(state.validators) // (get_slashing_multiplier(spec) + 1)
|
||||||
|
|
||||||
assert slashed_count > 10
|
assert slashed_count > 10
|
||||||
|
|
||||||
|
@ -134,7 +166,7 @@ def test_scaled_penalties(spec, state):
|
||||||
v = state.validators[i]
|
v = state.validators[i]
|
||||||
expected_penalty = (
|
expected_penalty = (
|
||||||
v.effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
|
v.effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
* (spec.PROPORTIONAL_SLASHING_MULTIPLIER * total_penalties)
|
* (get_slashing_multiplier(spec) * total_penalties)
|
||||||
// (total_balance)
|
// (total_balance)
|
||||||
* spec.EFFECTIVE_BALANCE_INCREMENT
|
* spec.EFFECTIVE_BALANCE_INCREMENT
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue