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

Penalties+activs+exits only at 64-epoch boundary #2192

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4822b1b
Penalties+activs+exits only at 64-epoch boundary
vbuterin Jan 27, 2021
3135020
Apply suggestions from code review
vbuterin Jan 28, 2021
6979d40
Some formatting fixes and more comments
vbuterin Jan 28, 2021
ac8ddcb
Merge branch 'accounting-reform' into vbuterin-patch-10
djrtwo Feb 1, 2021
9109ac2
working through testing on leak PR
djrtwo Feb 2, 2021
36f8a09
Merge branch 'dev' into vbuterin-patch-10
djrtwo Feb 4, 2021
2fe6f24
Merge branch 'vbuterin-patch-10' into leak-tests
djrtwo Feb 4, 2021
82b4dc1
toc
djrtwo Feb 4, 2021
e86b8f6
fix minor bug in get_leak_penalties function
djrtwo Feb 4, 2021
95d1459
workign through leak tests
djrtwo Feb 9, 2021
b345d4f
green tests for 64-epoch pr
djrtwo Feb 10, 2021
56d3a30
fix leak_epoch_counter bug and add sanity test
djrtwo Feb 15, 2021
60ffac5
Apply suggestions from code review from @hwwhww
djrtwo Feb 15, 2021
09faced
Apply suggestions from code review from @hwwhww
djrtwo Feb 15, 2021
409bf3f
Merge branch 'leak-tests' of github.com:ethereum/eth2.0-specs into le…
djrtwo Feb 15, 2021
dfce74e
leaking transitions to activation/exit period boundary after HF1
djrtwo Feb 16, 2021
7cf29db
Merge pull request #2194 from ethereum/leak-tests
djrtwo Feb 16, 2021
da61d57
fix activation/exit period to 64 epochs for mainnet config
djrtwo Feb 16, 2021
71103d2
Merge branch 'dev' into vbuterin-patch-10
djrtwo Feb 16, 2021
97128d2
Merge branch 'dev' into vbuterin-patch-10
djrtwo Feb 16, 2021
4d13522
Merge branch 'vbuterin-patch-10' of github.com:ethereum/eth2.0-specs …
djrtwo Feb 16, 2021
413e47a
Merge branch 'dev' into vbuterin-patch-10
djrtwo Feb 24, 2021
f4dbb40
Merge branch 'dev' into vbuterin-patch-10
djrtwo Mar 2, 2021
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
2 changes: 2 additions & 0 deletions configs/mainnet/lightclient_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ SYNC_SUBCOMMITTEE_SIZE: 64
# ---------------------------------------------------------------
# 2**8 (= 256)
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256
# 2**6 (= 64)
EPOCHS_PER_ACTIVATION_EXIT_PERIOD: 64


# Signature domains
Expand Down
2 changes: 2 additions & 0 deletions configs/minimal/lightclient_patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ SYNC_SUBCOMMITTEE_SIZE: 16
# ---------------------------------------------------------------
# [customized]
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8
# [customized]
EPOCHS_PER_ACTIVATION_EXIT_PERIOD: 2


# Signature domains
Expand Down
252 changes: 205 additions & 47 deletions specs/lightclient/beacon-chain.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions specs/lightclient/lightclient-fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
# Registry
validators=pre.validators,
balances=pre.balances,
leak_score=[0 for _ in range(len(pre.validators))],
# Randomness
randao_mixes=pre.randao_mixes,
# Slashings
Expand Down
101 changes: 77 additions & 24 deletions tests/core/pyspec/eth2spec/test/helpers/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,33 @@ def run_deltas(spec, state):
- source deltas ('source_deltas')
- target deltas ('target_deltas')
- head deltas ('head_deltas')
- inclusion delay deltas ('inclusion_delay_deltas')
- inactivity penalty deltas ('inactivity_penalty_deltas')
- if is_post_lightclient_patch(spec)
- regular penalty deltas ('regular_penalty_deltas')
- leak penalty deltas ('leak_penalty_deltas')
- else
- inclusion delay deltas ('inclusion_delay_deltas')
- inactivity penalty deltas ('inactivity_penalty_deltas')
"""
yield 'pre', state

if is_post_lightclient_patch(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_rewards(state, spec.TIMELY_SOURCE_FLAG_INDEX, spec.TIMELY_SOURCE_FLAG_NUMERATOR),
[0] * len(state.validators)
)

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_rewards(state, spec.TIMELY_HEAD_FLAG_INDEX, spec.TIMELY_HEAD_FLAG_NUMERATOR),
[0] * len(state.validators)
)

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_rewards(state, spec.TIMELY_TARGET_FLAG_INDEX, spec.TIMELY_TARGET_FLAG_NUMERATOR),
[0] * len(state.validators)
)

yield from run_attestation_component_deltas(
spec,
Expand All @@ -70,8 +83,12 @@ def get_target_deltas(state):
spec.get_matching_head_attestations,
'head_deltas',
)
yield from run_get_inclusion_delay_deltas(spec, state)
yield from run_get_inactivity_penalty_deltas(spec, state)
if is_post_lightclient_patch(spec):
yield from run_get_regular_penalties(spec, state)
yield from run_get_leak_penalties(spec, state)
else:
yield from run_get_inclusion_delay_deltas(spec, state)
yield from run_get_inactivity_penalty_deltas(spec, state)


def deltas_name_to_flag_index(spec, deltas_name):
Expand Down Expand Up @@ -118,10 +135,39 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a
assert penalties[index] == 0
else:
assert rewards[index] == 0
if enough_for_reward:
assert penalties[index] > 0
else:
if not enough_for_reward or is_post_lightclient_patch(spec):
assert penalties[index] == 0
else:
assert penalties[index] > 0


def run_get_regular_penalties(spec, state):
"""
Run ``get_regular_penalties``, yielding:
- penalty deltas (`[0...], penalties`)
"""
penalties = spec.get_regular_penalties(state)
rewards = [0] * len(state.validators)

yield 'regular_penalty_deltas', Deltas(rewards=rewards, penalties=penalties)

if not spec.is_activation_exit_period_boundary(state):
assert penalties == rewards


def run_get_leak_penalties(spec, state):
"""
Run ``get_leak_penalties``, yielding:
- penalty deltas (`[0...], penalties`)
"""
penalties = spec.get_leak_penalties(state)
rewards = [0] * len(state.validators)

yield 'leak_penalty_deltas', Deltas(rewards=rewards, penalties=penalties)

if not spec.is_activation_exit_period_boundary(state):
assert penalties == rewards
assert not any(rewards)


def run_get_inclusion_delay_deltas(spec, state):
Expand Down Expand Up @@ -178,18 +224,18 @@ def run_get_inactivity_penalty_deltas(spec, state):
Run ``get_inactivity_penalty_deltas``, yielding:
- inactivity penalty deltas ('inactivity_penalty_deltas')
"""
if is_post_lightclient_patch(spec):
# No inclusion_delay_deltas
yield 'inclusion_delay_deltas', Deltas(rewards=[0] * len(state.validators),
penalties=[0] * len(state.validators))
return

rewards, penalties = spec.get_inactivity_penalty_deltas(state)

yield 'inactivity_penalty_deltas', Deltas(rewards=rewards, penalties=penalties)

if not is_post_lightclient_patch(spec):
matching_attestations = spec.get_matching_target_attestations(state, spec.get_previous_epoch(state))
matching_attesting_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
else:
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())
matching_attestations = spec.get_matching_target_attestations(state, spec.get_previous_epoch(state))
matching_attesting_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)

eligible_indices = spec.get_eligible_validator_indices(state)
for index in range(len(state.validators)):
Expand All @@ -200,12 +246,9 @@ def run_get_inactivity_penalty_deltas(spec, state):

if spec.is_in_inactivity_leak(state):
# Compute base_penalty
if not is_post_lightclient_patch(spec):
cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH
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
cancel_base_rewards_per_epoch = spec.BASE_REWARDS_PER_EPOCH
base_reward = spec.get_base_reward(state, index)
base_penalty = cancel_base_rewards_per_epoch * base_reward - spec.get_proposer_reward(state, index)

if not has_enough_for_reward(spec, state, index):
assert penalties[index] == 0
Expand All @@ -225,6 +268,16 @@ def transition_state_to_leak(spec, state, epochs=None):
for _ in range(epochs):
next_epoch(spec, state)

# Leak penalties are only applied at activation/exit period boundaries after HF1.
# So transition to this period boundary to make the test interesting.
if is_post_lightclient_patch(spec) and not spec.is_activation_exit_period_boundary(state):
epochs_until_boundary = (
spec.EPOCHS_PER_ACTIVATION_EXIT_PERIOD
- spec.get_current_epoch(state) % spec.EPOCHS_PER_ACTIVATION_EXIT_PERIOD
)
for _ in range(epochs_until_boundary):
next_epoch(spec, state)


_cache_dict = LRU(size=10)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
next_epoch,
next_epoch_via_block,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
build_empty_block,
)
from eth2spec.test.helpers.sync_committee import (
compute_aggregate_sync_committee_signature,
Expand Down Expand Up @@ -73,3 +75,28 @@ def test_half_sync_committee_committee_genesis(spec, state):
@spec_state_test
def test_empty_sync_committee_committee_genesis(spec, state):
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0)


@with_all_phases_except([PHASE0, PHASE1])
@spec_state_test
def test_leak_epoch_counter(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)

if spec.is_activation_exit_period_boundary(state):
next_epoch_via_block(spec, state)

previous_leak_epoch_counter = state.leak_epoch_counter

yield 'pre', state

# Block transition to next epoch
block = build_empty_block(spec, state, slot=state.slot + spec.SLOTS_PER_EPOCH)
signed_block = state_transition_and_sign_block(spec, state, block)

yield 'blocks', [signed_block]
yield 'post', state

assert state.leak_epoch_counter == previous_leak_epoch_counter + 1
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,20 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)

proposer_index = spec.get_beacon_proposer_index(state)
pre_proposer_balance = get_balance(state, proposer_index)
pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices}
pre_slashing_balances = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices}
pre_slashing_effectives = {
slashed_index: state.validators[slashed_index].effective_balance
for slashed_index in slashed_indices
}

pre_withdrawalable_epochs = {
slashed_index: state.validators[slashed_index].withdrawable_epoch
for slashed_index in slashed_indices
}

total_proposer_rewards = sum(
balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
for balance in pre_slashings.values()
effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
for effective_balance in pre_slashing_effectives.values()
)

# Process slashing
Expand All @@ -61,7 +66,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
assert slashed_validator.withdrawable_epoch == expected_withdrawable_epoch
else:
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
assert get_balance(state, slashed_index) < pre_slashings[slashed_index]
assert get_balance(state, slashed_index) < pre_slashing_balances[slashed_index]

if proposer_index not in slashed_indices:
# gained whistleblower reward
Expand All @@ -71,7 +76,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
expected_balance = (
pre_proposer_balance
+ total_proposer_rewards
- pre_slashings[proposer_index] // get_min_slashing_penalty_quotient(spec)
- pre_slashing_effectives[proposer_index] // get_min_slashing_penalty_quotient(spec)
)

assert get_balance(state, proposer_index) == expected_balance
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
from eth2spec.test.context import (
spec_state_test, expect_assertion_error, always_bls, with_all_phases,
is_post_lightclient_patch,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import sign_voluntary_exit

Expand Down Expand Up @@ -76,8 +79,11 @@ def test_success_exit_queue(spec, state):

current_epoch = spec.get_current_epoch(state)

# exit `MAX_EXITS_PER_EPOCH`
initial_indices = spec.get_active_validator_indices(state, current_epoch)[:spec.get_validator_churn_limit(state)]
churn = spec.get_validator_churn_limit(state)
if is_post_lightclient_patch(spec):
churn = churn * spec.EPOCHS_PER_ACTIVATION_EXIT_PERIOD

initial_indices = spec.get_active_validator_indices(state, current_epoch)[:churn]

# Prepare a bunch of exits, based on the current state
exit_queue = []
Expand Down Expand Up @@ -106,9 +112,10 @@ def test_success_exit_queue(spec, state):
# when processing an additional exit, it results in an exit in a later epoch
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)

queue_increment = spec.EPOCHS_PER_ACTIVATION_EXIT_PERIOD if is_post_lightclient_patch(spec) else 1
assert (
state.validators[validator_index].exit_epoch ==
state.validators[initial_indices[0]].exit_epoch + 1
state.validators[initial_indices[0]].exit_epoch + queue_increment
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with, run_epoch_processing_to
)


def run_process_effective_balance_updates(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_effective_balance_updates')
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to


@with_all_phases
Expand Down Expand Up @@ -44,7 +38,9 @@ def test_effective_balance_hysteresis(spec, state):
state.validators[i].effective_balance = pre_eff
state.balances[i] = bal

yield from run_process_effective_balance_updates(spec, state)
yield 'pre', state
spec.process_effective_balance_updates(state)
yield 'post', state

for i, (_, _, post_eff, name) in enumerate(cases):
assert state.validators[i].effective_balance == post_eff, name
Loading