Skip to content

Commit

Permalink
Altair cosmetic cleanups plus a couple substantive changes
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinDrake committed Mar 15, 2021
1 parent ea9f3f1 commit e7ebd08
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 138 deletions.
10 changes: 5 additions & 5 deletions configs/mainnet/altair.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CONFIG_NAME: "mainnet"

# Updated penalty values
# ---------------------------------------------------------------
# 3 * 2**24) (= 50,331,648)
# 3 * 2**24 (= 50,331,648)
ALTAIR_INACTIVITY_PENALTY_QUOTIENT: 50331648
# 2**6 (= 64)
ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT: 64
Expand All @@ -14,12 +14,12 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2

# Misc
# ---------------------------------------------------------------
# 2**10 (=1,024)
# 2**10 (= 1,024)
SYNC_COMMITTEE_SIZE: 1024
# 2**6 (=64)
# 2**6 (= 64)
SYNC_SUBCOMMITTEE_SIZE: 64
# 2**2 (=4)
LEAK_SCORE_BIAS: 4
# 2**2 (= 4)
INACTIVITY_SCORE_BIAS: 4


# Time parameters
Expand Down
6 changes: 3 additions & 3 deletions configs/minimal/altair.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CONFIG_NAME: "minimal"

# Updated penalty values
# ---------------------------------------------------------------
# 3 * 2**24) (= 50,331,648)
# 3 * 2**24 (= 50,331,648)
ALTAIR_INACTIVITY_PENALTY_QUOTIENT: 50331648
# 2**6 (= 64)
ALTAIR_MIN_SLASHING_PENALTY_QUOTIENT: 64
Expand All @@ -18,8 +18,8 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2
SYNC_COMMITTEE_SIZE: 32
# [customized]
SYNC_SUBCOMMITTEE_SIZE: 16
# 2**2 (=4)
LEAK_SCORE_BIAS: 4
# 2**2 (= 4)
INACTIVITY_SCORE_BIAS: 4


# Time parameters
Expand Down
157 changes: 66 additions & 91 deletions specs/altair/beacon-chain.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions specs/altair/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
previous_justified_checkpoint=pre.previous_justified_checkpoint,
current_justified_checkpoint=pre.current_justified_checkpoint,
finalized_checkpoint=pre.finalized_checkpoint,
# Leak
leak_scores=[0 for _ in range(len(pre.validators))],
# Inactivity
inactivity_scores=[0 for _ in range(len(pre.validators))],
)
# Fill in sync committees
post.current_sync_committee = get_sync_committee(post, get_current_epoch(post))
Expand Down
2 changes: 1 addition & 1 deletion specs/phase0/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ The following values are (non-configurable) constants used throughout the specif
| `WHISTLEBLOWER_REWARD_QUOTIENT` | `uint64(2**9)` (= 512) |
| `PROPOSER_REWARD_QUOTIENT` | `uint64(2**3)` (= 8) |
| `INACTIVITY_PENALTY_QUOTIENT` | `uint64(2**26)` (= 67,108,864) |
| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**7)` (=128) |
| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**7)` (= 128) |
| `PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(1)` |

- The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**13` epochs (about 36 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. Note this value will be upgraded to `2**24` after Phase 0 mainnet stabilizes to provide a faster recovery in the event of an inactivity leak.
Expand Down
2 changes: 1 addition & 1 deletion specs/phase0/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,5 +647,5 @@ A validator client should be considered standalone and should consider the beaco
1) Private keys -- private keys should be protected from being exported accidentally or by an attacker.
2) Slashing -- before a validator client signs a message it should validate the data, check it against a local slashing database (do not sign a slashable attestation or block) and update its internal slashing database with the newly signed object.
3) Recovered validator -- Recovering a validator from a private key will result in an empty local slashing db. Best practice is to import (from a trusted source) that validator's attestation history. See [EIP 3076](https://github.com/ethereum/EIPs/pull/3076/files) for a standard slashing interchange format.
4) Far future signing requests -- A validator client can be requested to sign a far into the future attestation, resulting in a valid non-slashable request. If the validator client signs this message, it will result in it blocking itself from attesting any other attestation until the beacon-chain reaches that far into the future epoch. This will result in an inactivity leak and potential ejection due to low balance.
4) Far future signing requests -- A validator client can be requested to sign a far into the future attestation, resulting in a valid non-slashable request. If the validator client signs this message, it will result in it blocking itself from attesting any other attestation until the beacon-chain reaches that far into the future epoch. This will result in an inactivity penalty and potential ejection due to low balance.
A validator client should prevent itself from signing such requests by: a) keeping a local time clock if possible and following best practices to stop time server attacks and b) refusing to sign, by default, any message that has a large (>6h) gap from the current slashing protection database indicated a time "jump" or a long offline event. The administrator can manually override this protection to restart the validator after a genuine long offline event.
8 changes: 4 additions & 4 deletions tests/core/pyspec/eth2spec/test/altair/sanity/test_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ def test_empty_sync_committee_committee_genesis(spec, state):

@with_all_phases_except([PHASE0, PHASE1])
@spec_state_test
def test_leak_scores(spec, state):
def test_inactivity_scores(spec, state):
for _ in range(spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2):
next_epoch_via_block(spec, state)

assert spec.is_in_inactivity_leak(state)
previous_leak_scores = state.leak_scores.copy()
previous_inactivity_scores = state.inactivity_scores.copy()

yield 'pre', state

Expand All @@ -95,5 +95,5 @@ def test_leak_scores(spec, state):
yield 'blocks', [signed_block]
yield 'post', state

for pre, post in zip(previous_leak_scores, state.leak_scores):
assert post == pre + spec.LEAK_SCORE_BIAS
for pre, post in zip(previous_inactivity_scores, state.inactivity_scores):
assert post == pre + spec.INACTIVITY_SCORE_BIAS
13 changes: 6 additions & 7 deletions tests/core/pyspec/eth2spec/test/helpers/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ def run_deltas(spec, state):

if is_post_altair(spec):
def get_source_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_FLAG_NUMERATOR)
return spec.get_flag_index_deltas(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_FLAG_NUMERATOR)

def get_head_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_FLAG_NUMERATOR)
return spec.get_flag_index_deltas(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_FLAG_NUMERATOR)

def get_target_deltas(state):
return spec.get_flag_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_FLAG_NUMERATOR)
return spec.get_flag_index_deltas(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_FLAG_NUMERATOR)

yield from run_attestation_component_deltas(
spec,
Expand Down Expand Up @@ -191,7 +191,6 @@ def run_get_inactivity_penalty_deltas(spec, state):
matching_attesting_indices = spec.get_unslashed_participating_indices(
state, spec.TIMELY_TARGET_FLAG_INDEX, spec.get_previous_epoch(state)
)
reward_numerator_sum = sum(numerator for (_, numerator) in spec.get_flag_indices_and_numerators())

eligible_indices = spec.get_eligible_validator_indices(state)
for index in range(len(state.validators)):
Expand All @@ -207,7 +206,7 @@ def run_get_inactivity_penalty_deltas(spec, state):
base_reward = spec.get_base_reward(state, index)
base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index)
else:
base_penalty = spec.get_base_reward(state, index) * reward_numerator_sum // spec.FLAG_DENOMINATOR
base_penalty = sum(spec.get_base_reward(state, index) * numerator // spec.FLAG_DENOMINATOR for (_, numerator) in spec.get_flag_indices_and_numerators())

if not has_enough_for_reward(spec, state, index):
assert penalties[index] == 0
Expand All @@ -221,7 +220,7 @@ def run_get_inactivity_penalty_deltas(spec, state):

def transition_state_to_leak(spec, state, epochs=None):
if epochs is None:
# +1 to trigger leak_score transitions
# +1 to trigger inactivity_score transitions
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 1
assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY

Expand All @@ -232,7 +231,7 @@ def transition_state_to_leak(spec, state, epochs=None):
_cache_dict = LRU(size=10)


def leaking(epochs=None):
def inactivity_penalty_active(epochs=None):

def deco(fn):
def entry(*args, spec, state, **kw):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
sign_attestation,
prepare_state_with_attestations,
)
from eth2spec.test.helpers.rewards import leaking
from eth2spec.test.helpers.rewards import inactivity_penalty_active
from eth2spec.test.helpers.attester_slashings import get_indexed_attestation_participants
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
from random import Random
Expand Down Expand Up @@ -205,7 +205,7 @@ def test_almost_empty_attestations(spec, state):

@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_almost_empty_attestations_with_leak(spec, state):
rng = Random(1234)
yield from run_with_participation(spec, state, lambda slot, comm_index, comm: rng.sample(comm, 1))
Expand All @@ -220,7 +220,7 @@ def test_random_fill_attestations(spec, state):

@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_random_fill_attestations_with_leak(spec, state):
rng = Random(4567)
yield from run_with_participation(spec, state, lambda slot, comm_index, comm: rng.sample(comm, len(comm) // 3))
Expand All @@ -235,7 +235,7 @@ def test_almost_full_attestations(spec, state):

@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_almost_full_attestations_with_leak(spec, state):
rng = Random(8901)
yield from run_with_participation(spec, state, lambda slot, comm_index, comm: rng.sample(comm, len(comm) - 1))
Expand All @@ -249,7 +249,7 @@ def test_full_attestation_participation(spec, state):

@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_attestation_participation_with_leak(spec, state):
yield from run_with_participation(spec, state, lambda slot, comm_index, comm: comm)

Expand Down
38 changes: 19 additions & 19 deletions tests/core/pyspec/eth2spec/test/phase0/rewards/test_leak.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,81 @@
from eth2spec.test.context import PHASE0, PHASE1, with_all_phases, with_phases, spec_state_test
from eth2spec.test.helpers.rewards import leaking
from eth2spec.test.helpers.rewards import inactivity_penalty_active
import eth2spec.test.helpers.rewards as rewards_helpers


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_empty_leak(spec, state):
yield from rewards_helpers.run_test_empty(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_leak(spec, state):
yield from rewards_helpers.run_test_full_all_correct(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_half_full_leak(spec, state):
yield from rewards_helpers.run_test_half_full(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_quarter_full_leak(spec, state):
yield from rewards_helpers.run_test_partial(spec, state, 0.25)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_but_partial_participation_leak(spec, state):
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state)


@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_one_attestation_one_correct_leak(spec, state):
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_with_not_yet_activated_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_with_exited_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_exited_validators(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_with_slashed_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_slashed_validators(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_some_very_low_effective_balances_that_attested_leak(spec, state):
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(spec, state)


@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_some_very_low_effective_balances_that_did_not_attest_leak(spec, state):
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(spec, state)

Expand All @@ -89,7 +89,7 @@ def test_some_very_low_effective_balances_that_did_not_attest_leak(spec, state):

@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_half_correct_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
Expand All @@ -101,7 +101,7 @@ def test_full_half_correct_target_incorrect_head_leak(spec, state):

@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_correct_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
Expand All @@ -113,7 +113,7 @@ def test_full_correct_target_incorrect_head_leak(spec, state):

@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_half_incorrect_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
Expand All @@ -125,7 +125,7 @@ def test_full_half_incorrect_target_incorrect_head_leak(spec, state):

@with_phases([PHASE0, PHASE1])
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_half_incorrect_target_correct_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
Expand All @@ -137,20 +137,20 @@ def test_full_half_incorrect_target_correct_head_leak(spec, state):

@with_all_phases
@spec_state_test
@leaking()
@inactivity_penalty_active()
def test_full_random_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)


@with_all_phases
@spec_state_test
@leaking(epochs=5)
@inactivity_penalty_active(epochs=5)
def test_full_random_five_epoch_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)


@with_all_phases
@spec_state_test
@leaking(epochs=10)
@inactivity_penalty_active(epochs=10)
def test_full_random_ten_epoch_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)

0 comments on commit e7ebd08

Please sign in to comment.