Merge pull request #2698 from ethereum/merge-penalties

update penalty params for Merge
This commit is contained in:
Danny Ryan 2021-10-31 09:59:54 -06:00 committed by GitHub
commit 77fe450f21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 154 additions and 16 deletions

View File

@ -1,5 +1,14 @@
# Mainnet preset - The Merge
# Updated penalty values
# ---------------------------------------------------------------
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT_MERGE: 16777216
# 2**5 (= 32)
MIN_SLASHING_PENALTY_QUOTIENT_MERGE: 32
# 3
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3
# Execution
# ---------------------------------------------------------------
# 2**30 (= 1,073,741,824)

View File

@ -1,5 +1,14 @@
# Minimal preset - The Merge
# Updated penalty values
# ---------------------------------------------------------------
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT_MERGE: 16777216
# 2**5 (= 32)
MIN_SLASHING_PENALTY_QUOTIENT_MERGE: 32
# 3
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3
# Execution
# ---------------------------------------------------------------
# 2**30 (= 1,073,741,824)

View File

@ -12,6 +12,8 @@
- [Custom types](#custom-types)
- [Constants](#constants)
- [Execution](#execution)
- [Preset](#preset)
- [Updated penalty values](#updated-penalty-values)
- [Configuration](#configuration)
- [Transition settings](#transition-settings)
- [Containers](#containers)
@ -28,13 +30,19 @@
- [`is_execution_enabled`](#is_execution_enabled)
- [Misc](#misc)
- [`compute_timestamp_at_slot`](#compute_timestamp_at_slot)
- [Beacon state accessors](#beacon-state-accessors)
- [Modified `get_inactivity_penalty_deltas`](#modified-get_inactivity_penalty_deltas)
- [Beacon state mutators](#beacon-state-mutators)
- [Modified `slash_validator`](#modified-slash_validator)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Execution engine](#execution-engine)
- [`execute_payload`](#execute_payload)
- [Block processing](#block-processing)
- [Execution payload processing](#execution-payload-processing)
- [`is_valid_gas_limit`](#is_valid_gas_limit)
- [`process_execution_payload`](#process_execution_payload)
- [Execution payload](#execution-payload)
- [`is_valid_gas_limit`](#is_valid_gas_limit)
- [`process_execution_payload`](#process_execution_payload)
- [Epoch processing](#epoch-processing)
- [Slashings](#slashings)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -42,7 +50,10 @@
## Introduction
This patch adds transaction execution to the beacon chain as part of the Merge fork.
This upgrade adds transaction execution to the beacon chain as part of the Merge fork.
Additionally, this upgrade introduces the following minor changes:
* Penalty parameter updates to their planned maximally punitive values
## Custom types
@ -66,6 +77,20 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
| `MIN_GAS_LIMIT` | `uint64(5000)` (= 5,000) |
| `MAX_EXTRA_DATA_BYTES` | `2**5` (= 32) |
## Preset
### Updated penalty values
The Merge updates a few configuration values to move penalty parameters to their final, maximum security values.
*Note*: The spec does *not* override previous configuration values but instead creates new values and replaces usage throughout.
| Name | Value |
| - | - |
| `INACTIVITY_PENALTY_QUOTIENT_MERGE` | `uint64(2**24)` (= 16,777,216) |
| `MIN_SLASHING_PENALTY_QUOTIENT_MERGE` | `uint64(2**5)` (= 32) |
| `PROPORTIONAL_SLASHING_MULTIPLIER_MERGE` | `uint64(3)` |
## Configuration
### Transition settings
@ -223,6 +248,64 @@ def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64:
return uint64(state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT)
```
### Beacon state accessors
#### Modified `get_inactivity_penalty_deltas`
*Note*: The function `get_inactivity_penalty_deltas` is modified to use `INACTIVITY_PENALTY_QUOTIENT_MERGE`.
```python
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
"""
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))]
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):
if index not in matching_target_indices:
penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index]
# [Modified in Merge]
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE
penalties[index] += Gwei(penalty_numerator // penalty_denominator)
return rewards, penalties
```
### Beacon state mutators
#### Modified `slash_validator`
*Note*: The function `slash_validator` is modified to use `MIN_SLASHING_PENALTY_QUOTIENT_MERGE`.
```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
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE # [Modified in Merge]
decrease_balance(state, slashed_index, slashing_penalty)
# 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_WEIGHT // WEIGHT_DENOMINATOR)
increase_balance(state, proposer_index, proposer_reward)
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
```
## Beacon chain state transition function
### Execution engine
@ -262,9 +345,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_sync_aggregate(state, block.body.sync_aggregate)
```
### Execution payload processing
#### Execution payload
#### `is_valid_gas_limit`
##### `is_valid_gas_limit`
```python
def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader) -> bool:
@ -287,7 +370,7 @@ def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader
return True
```
#### `process_execution_payload`
##### `process_execution_payload`
```python
def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None:
@ -322,6 +405,28 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
)
```
### Epoch processing
#### Slashings
*Note*: The function `process_slashings` is modified to use `PROPORTIONAL_SLASHING_MULTIPLIER_MERGE`.
```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) * PROPORTIONAL_SLASHING_MULTIPLIER_MERGE, # [Modified in Merge]
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)
```
## Testing
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Merge testing only.

View File

@ -1,4 +1,4 @@
from eth2spec.test.context import is_post_altair
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.helpers.block_header import sign_block_header
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.state import get_balance
@ -9,7 +9,9 @@ from eth2spec.test.helpers.sync_committee import (
def get_min_slashing_penalty_quotient(spec):
if is_post_altair(spec):
if is_post_merge(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_MERGE
elif is_post_altair(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR
else:
return spec.MIN_SLASHING_PENALTY_QUOTIENT

View File

@ -2,7 +2,7 @@ from random import Random
from lru import LRU
from eth2spec.phase0.mainnet import VALIDATOR_REGISTRY_LIMIT # equal everywhere, fine to import
from eth2spec.test.context import is_post_altair
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.helpers.state import (
next_epoch,
)
@ -21,6 +21,15 @@ class Deltas(Container):
penalties: List[uint64, VALIDATOR_REGISTRY_LIMIT]
def get_inactivity_penalty_quotient(spec):
if is_post_merge(spec):
return spec.INACTIVITY_PENALTY_QUOTIENT_MERGE
elif is_post_altair(spec):
return spec.INACTIVITY_PENALTY_QUOTIENT_ALTAIR
else:
return spec.INACTIVITY_PENALTY_QUOTIENT
def has_enough_for_reward(spec, state, index):
"""
Check if base_reward will be non-zero.
@ -45,7 +54,7 @@ def has_enough_for_leak_penalty(spec, state, index):
if is_post_altair(spec):
return (
state.validators[index].effective_balance * state.inactivity_scores[index]
> spec.config.INACTIVITY_SCORE_BIAS * spec.INACTIVITY_PENALTY_QUOTIENT_ALTAIR
> spec.config.INACTIVITY_SCORE_BIAS * get_inactivity_penalty_quotient(spec)
)
else:
return (
@ -266,7 +275,7 @@ def run_get_inactivity_penalty_deltas(spec, state):
else:
# copied from spec:
penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index]
penalty_denominator = spec.config.INACTIVITY_SCORE_BIAS * spec.INACTIVITY_PENALTY_QUOTIENT_ALTAIR
penalty_denominator = spec.config.INACTIVITY_SCORE_BIAS * get_inactivity_penalty_quotient(spec)
assert penalties[index] == penalty_numerator // penalty_denominator

View File

@ -1,5 +1,5 @@
from random import Random
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_altair
from eth2spec.test.context import spec_state_test, with_all_phases, is_post_altair, is_post_merge
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with, run_epoch_processing_to
)
@ -31,7 +31,9 @@ def slash_validators(spec, state, indices, out_epochs):
def get_slashing_multiplier(spec):
if is_post_altair(spec):
if is_post_merge(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER_MERGE
elif is_post_altair(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR
else:
return spec.PROPORTIONAL_SLASHING_MULTIPLIER

View File

@ -1,7 +1,7 @@
from eth2spec.test.context import (
spec_state_test,
with_all_phases,
is_post_altair,
is_post_altair, is_post_merge,
)
from eth2spec.test.helpers.constants import MAX_UINT_64
@ -52,7 +52,9 @@ def test_hysteresis_quotient(spec, state):
@spec_state_test
def test_incentives(spec, state):
# Ensure no ETH is minted in slash_validator
if is_post_altair(spec):
if is_post_merge(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_MERGE <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
elif is_post_altair(spec):
assert spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR <= spec.WHISTLEBLOWER_REWARD_QUOTIENT
else:
assert spec.MIN_SLASHING_PENALTY_QUOTIENT <= spec.WHISTLEBLOWER_REWARD_QUOTIENT