port testing

This commit is contained in:
Danny Ryan 2021-03-09 15:16:26 -07:00
parent 70a2834058
commit e2abdb74ae
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
9 changed files with 125 additions and 69 deletions

View File

@ -18,6 +18,8 @@ HF1_PROPORTIONAL_SLASHING_MULTIPLIER: 2
SYNC_COMMITTEE_SIZE: 1024
# 2**6 (=64)
SYNC_SUBCOMMITTEE_SIZE: 64
# 2**2 (=4)
LEAK_SCORE_BIAS: 4
# Time parameters

View File

@ -18,6 +18,8 @@ HF1_PROPORTIONAL_SLASHING_MULTIPLIER: 2
SYNC_COMMITTEE_SIZE: 32
# [customized]
SYNC_SUBCOMMITTEE_SIZE: 16
# 2**2 (=4)
LEAK_SCORE_BIAS: 4
# Time parameters

View File

@ -18,7 +18,7 @@
- [Time parameters](#time-parameters)
- [Domain types](#domain-types)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [Modified containers](#modified-containers)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconState`](#beaconstate)
- [New containers](#new-containers)
@ -135,10 +135,7 @@ This patch updates a few configuration values to move penalty constants toward t
## Containers
### Extended containers
*Note*: Extended SSZ containers inherit all fields from the parent in the original
order and append any additional fields to the end.
### Modified containers
#### `BeaconBlockBody`
@ -195,7 +192,7 @@ class BeaconState(Container):
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Leak
leak_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT]
leak_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] # [New in HF1]
```
### New containers

View File

@ -42,6 +42,7 @@ After `process_slots` of Phase 0 finishes, if `state.slot == LIGHTCLIENT_PATCH_F
def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
epoch = get_current_epoch(pre)
post = BeaconState(
# Versioning
genesis_time=pre.genesis_time,
genesis_validators_root=pre.genesis_validators_root,
slot=pre.slot,
@ -67,13 +68,15 @@ def upgrade_to_lightclient_patch(pre: phase0.BeaconState) -> BeaconState:
# Slashings
slashings=pre.slashings,
# Participation
previous_epoch_participation=[ParticipationFlags(0) for _ in range(len(pre.validators))],
current_epoch_participation=[ParticipationFlags(0) for _ in range(len(pre.validators))],
previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
# Finality
justification_bits=pre.justification_bits,
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))],
)
# Fill in sync committees
post.current_sync_committee = get_sync_committee(post, get_current_epoch(post))

View File

@ -34,7 +34,8 @@ def run_deltas(spec, state):
- source deltas ('source_deltas')
- target deltas ('target_deltas')
- head deltas ('head_deltas')
- inclusion delay deltas ('inclusion_delay_deltas')
- not if is_post_lightclient_patch(spec)
- inclusion delay deltas ('inclusion_delay_deltas')
- inactivity penalty deltas ('inactivity_penalty_deltas')
"""
yield 'pre', state
@ -70,7 +71,8 @@ def run_deltas(spec, state):
spec.get_matching_head_attestations,
'head_deltas',
)
yield from run_get_inclusion_delay_deltas(spec, state)
if not is_post_lightclient_patch(spec):
yield from run_get_inclusion_delay_deltas(spec, state)
yield from run_get_inactivity_penalty_deltas(spec, state)
@ -219,7 +221,8 @@ def run_get_inactivity_penalty_deltas(spec, state):
def transition_state_to_leak(spec, state, epochs=None):
if epochs is None:
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
# +1 to trigger leak_score transitions
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 1
assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
for _ in range(epochs):

View File

@ -2,9 +2,11 @@ import random
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,
@ -73,3 +75,25 @@ 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_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()
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
for pre, post in zip(previous_leak_scores, state.leak_scores):
assert post == pre + spec.LEAK_SCORE_BIAS

View File

@ -1,5 +1,9 @@
import random
from eth2spec.test.context import (
spec_state_test, expect_assertion_error, always_bls, with_all_phases
spec_state_test, expect_assertion_error, always_bls, with_all_phases,
with_custom_state, spec_test, single_phase,
low_balances, misc_balances,
)
from eth2spec.test.helpers.attestations import sign_indexed_attestation
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
@ -32,15 +36,19 @@ 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
@ -61,7 +69,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
@ -71,7 +79,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
@ -118,6 +126,41 @@ def test_success_already_exited_recent(spec, state):
yield from run_attester_slashing_processing(spec, state, attester_slashing)
@with_all_phases
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase
def test_success_low_balances(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
yield from run_attester_slashing_processing(spec, state, attester_slashing)
@with_all_phases
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase
def test_success_misc_balances(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
yield from run_attester_slashing_processing(spec, state, attester_slashing)
@with_all_phases
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase
def test_success_with_effective_balance_disparity(spec, state):
# Jitter balances to be different from effective balances
for i in range(len(state.balances)):
pre = int(state.balances[i])
state.balances[i] += random.randrange(max(pre - 5000, 0), pre + 5000)
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
yield from run_attester_slashing_processing(spec, state, attester_slashing)
@with_all_phases
@spec_state_test
@always_bls

View File

@ -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
@ -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

View File

@ -27,6 +27,31 @@ def run_process_rewards_and_penalties(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
def validate_resulting_balances(spec, pre_state, post_state, attestations):
attesting_indices = spec.get_unslashed_attesting_indices(post_state, attestations)
current_epoch = spec.get_current_epoch(post_state)
for index in range(len(pre_state.validators)):
if not spec.is_active_validator(pre_state.validators[index], current_epoch):
assert post_state.balances[index] == pre_state.balances[index]
elif not is_post_lightclient_patch(spec):
proposer_indices = [a.proposer_index for a in post_state.previous_epoch_attestations]
if spec.is_in_inactivity_leak(post_state):
# Proposers can still make money during a leak before LIGHTCLIENT_PATCH
if index in proposer_indices and index in attesting_indices:
assert post_state.balances[index] > pre_state.balances[index]
elif index in attesting_indices:
# If not proposer but participated optimally, should have exactly neutral balance
assert post_state.balances[index] == pre_state.balances[index]
else:
assert post_state.balances[index] < pre_state.balances[index]
else:
if index in attesting_indices:
assert post_state.balances[index] > pre_state.balances[index]
else:
assert post_state.balances[index] < pre_state.balances[index]
@with_all_phases
@spec_state_test
def test_genesis_epoch_no_attestations_no_penalties(spec, state):
@ -100,19 +125,10 @@ def test_full_attestations_misc_balances(spec, state):
yield from run_process_rewards_and_penalties(spec, state)
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
assert len(attesting_indices) > 0
assert len(attesting_indices) != len(pre_state.validators)
assert any(v.effective_balance != spec.MAX_EFFECTIVE_BALANCE for v in state.validators)
for index in range(len(pre_state.validators)):
if index in attesting_indices:
assert state.balances[index] > pre_state.balances[index]
elif spec.is_active_validator(pre_state.validators[index], spec.compute_epoch_at_slot(state.slot)):
assert state.balances[index] < pre_state.balances[index]
else:
assert state.balances[index] == pre_state.balances[index]
validate_resulting_balances(spec, pre_state, state, attestations)
# Check if base rewards are consistent with effective balance.
brs = {}
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
for index in attesting_indices:
br = spec.get_base_reward(state, index)
if br in brs:
@ -146,8 +162,7 @@ def test_no_attestations_all_penalties(spec, state):
yield from run_process_rewards_and_penalties(spec, state)
for index in range(len(pre_state.validators)):
assert state.balances[index] < pre_state.balances[index]
validate_resulting_balances(spec, pre_state, state, [])
def run_with_participation(spec, state, participation_fn):
@ -161,35 +176,12 @@ def run_with_participation(spec, state, participation_fn):
attestations = prepare_state_with_attestations(spec, state, participation_fn=participation_tracker)
pre_state = state.copy()
if not is_post_lightclient_patch(spec):
proposer_indices = [a.proposer_index for a in state.previous_epoch_attestations]
else:
sync_committee_indices = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
yield from run_process_rewards_and_penalties(spec, state)
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
assert len(attesting_indices) == len(participated)
for index in range(len(pre_state.validators)):
if spec.is_in_inactivity_leak(state):
# Proposers can still make money during a leak before LIGHTCLIENT_PATCH
if not is_post_lightclient_patch(spec) and index in proposer_indices and index in participated:
assert state.balances[index] > pre_state.balances[index]
elif index in attesting_indices:
if is_post_lightclient_patch(spec) and index in sync_committee_indices:
# The sync committee reward has not been canceled, so the sync committee participants still earn it
assert state.balances[index] >= pre_state.balances[index]
else:
# If not proposer but participated optimally, should have exactly neutral balance
assert state.balances[index] == pre_state.balances[index]
else:
assert state.balances[index] < pre_state.balances[index]
else:
if index in participated:
assert state.balances[index] > pre_state.balances[index]
else:
assert state.balances[index] < pre_state.balances[index]
validate_resulting_balances(spec, pre_state, state, attestations)
@with_all_phases
@ -438,10 +430,4 @@ def test_attestations_some_slashed(spec, state):
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
assert len(attesting_indices) > 0
assert len(attesting_indices_before_slashings) - len(attesting_indices) == spec.MIN_PER_EPOCH_CHURN_LIMIT
for index in range(len(pre_state.validators)):
if index in attesting_indices:
# non-slashed attester should gain reward
assert state.balances[index] > pre_state.balances[index]
else:
# Slashed non-proposer attester should have penalty
assert state.balances[index] < pre_state.balances[index]
validate_resulting_balances(spec, pre_state, state, attestations)