Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update penalty params for Merge #2698

Merged
merged 4 commits into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions presets/mainnet/merge.yaml
Original file line number Diff line number Diff line change
@@ -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
# 2
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3

# Execution
# ---------------------------------------------------------------
# 2**30 (= 1,073,741,824)
Expand Down
9 changes: 9 additions & 0 deletions presets/minimal/merge.yaml
Original file line number Diff line number Diff line change
@@ -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
# 2
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
PROPORTIONAL_SLASHING_MULTIPLIER_MERGE: 3

# Execution
# ---------------------------------------------------------------
# 2**30 (= 1,073,741,824)
Expand Down
109 changes: 103 additions & 6 deletions specs/merge/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 -->
Expand Down Expand Up @@ -66,6 +74,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
Expand Down Expand Up @@ -223,6 +245,62 @@ def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64:
return uint64(state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT)
```

### Beacon state accessors
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Altair is frozen, but it might make sense to go for code-reuse anyway by not copying it over. That said, I'm also kind of happy we can easily extract a version of the spec that doesn't contain any legacy preset/config options, for new client implementations to join.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by instead providing a config getter function -- e.g. get_inactivity_penalty_quotient(epoch) or something?

yeah, I see the argument on both sides ..


#### 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]
penalty_denominator = INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_MERGE
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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
decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_MERGE)
djrtwo marked this conversation as resolved.
Show resolved Hide resolved

# 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
Expand Down Expand Up @@ -262,9 +340,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:
Expand All @@ -287,7 +365,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:
Expand Down Expand Up @@ -322,6 +400,25 @@ 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, total_balance)
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down
6 changes: 4 additions & 2 deletions tests/core/pyspec/eth2spec/test/helpers/proposer_slashings.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,7 +9,9 @@


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
Expand Down
15 changes: 12 additions & 3 deletions tests/core/pyspec/eth2spec/test/helpers/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
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,
)
Expand All @@ -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.
Expand All @@ -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 (
Expand Down Expand Up @@ -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


Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down