add process_registry_updates tests for scaled churn limit
This commit is contained in:
parent
4faff4f899
commit
43e79a7ee0
|
@ -58,8 +58,8 @@ INACTIVITY_SCORE_RECOVERY_RATE: 16
|
|||
EJECTION_BALANCE: 16000000000
|
||||
# 2**2 (= 4)
|
||||
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
# 2**16 (= 65,536)
|
||||
CHURN_LIMIT_QUOTIENT: 65536
|
||||
# [customized] scale queue churn at much lower validator counts for testing
|
||||
CHURN_LIMIT_QUOTIENT: 32
|
||||
|
||||
|
||||
# Deposit contract
|
||||
|
|
|
@ -126,6 +126,17 @@ def default_balances(spec):
|
|||
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
|
||||
|
||||
|
||||
def scaled_churn_balances(spec):
|
||||
"""
|
||||
Helper method to create enough validators to scale the churn limit.
|
||||
(This is *firmly* over the churn limit -- thus the +2 instead of just +1)
|
||||
See the second argument of ``max`` in ``get_validator_churn_limit``.
|
||||
Usage: `@with_custom_state(balances_fn=scaled_churn_balances, ...)`
|
||||
"""
|
||||
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (2 + spec.config.MIN_PER_EPOCH_CHURN_LIMIT)
|
||||
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
|
||||
|
||||
|
||||
with_state = with_custom_state(default_balances, default_activation_threshold)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
from eth2spec.test.helpers.deposits import mock_deposit
|
||||
from eth2spec.test.helpers.state import next_epoch, next_slots
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.context import (
|
||||
spec_test, spec_state_test,
|
||||
with_all_phases, single_phase,
|
||||
with_custom_state,
|
||||
scaled_churn_balances,
|
||||
)
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
|
||||
|
||||
|
@ -112,9 +117,7 @@ def test_activation_queue_sorting(spec, state):
|
|||
assert state.validators[churn_limit - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_efficiency(spec, state):
|
||||
def run_test_activation_queue_efficiency(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
mock_activations = churn_limit * 2
|
||||
|
||||
|
@ -128,23 +131,43 @@ def test_activation_queue_efficiency(spec, state):
|
|||
|
||||
state.finalized_checkpoint.epoch = epoch + 1
|
||||
|
||||
# Churn limit could have changed given the active vals removed via `mock_deposit`
|
||||
churn_limit_0 = spec.get_validator_churn_limit(state)
|
||||
|
||||
# Run first registry update. Do not yield test vectors
|
||||
for _ in run_process_registry_updates(spec, state):
|
||||
pass
|
||||
|
||||
# Half should churn in first run of registry update
|
||||
for i in range(mock_activations):
|
||||
if i < mock_activations // 2:
|
||||
if i < churn_limit_0:
|
||||
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
|
||||
else:
|
||||
assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# Second half should churn in second run of registry update
|
||||
churn_limit_1 = spec.get_validator_churn_limit(state)
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
for i in range(mock_activations):
|
||||
for i in range(churn_limit_0 + churn_limit_1):
|
||||
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_efficiency_min(spec, state):
|
||||
assert spec.get_validator_churn_limit(state) == spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
yield from run_test_activation_queue_efficiency(spec, state)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_activation_queue_efficiency_scaled(spec, state):
|
||||
assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
yield from run_test_activation_queue_efficiency(spec, state)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_ejection(spec, state):
|
||||
|
@ -165,9 +188,7 @@ def test_ejection(spec, state):
|
|||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_ejection_past_churn_limit(spec, state):
|
||||
def run_test_ejection_past_churn_limit(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
|
||||
# try to eject more than per-epoch churn limit
|
||||
|
@ -184,58 +205,130 @@ def test_ejection_past_churn_limit(spec, state):
|
|||
# first third ejected in normal speed
|
||||
if i < mock_ejections // 3:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch
|
||||
# second thirdgets delayed by 1 epoch
|
||||
# second third gets delayed by 1 epoch
|
||||
elif mock_ejections // 3 <= i < mock_ejections * 2 // 3:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch + 1
|
||||
# second thirdgets delayed by 2 epochs
|
||||
# final third gets delayed by 2 epochs
|
||||
else:
|
||||
assert state.validators[i].exit_epoch == expected_ejection_epoch + 2
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_activation_and_ejection(spec, state):
|
||||
def test_ejection_past_churn_limit_min(spec, state):
|
||||
assert spec.get_validator_churn_limit(state) == spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
yield from run_test_ejection_past_churn_limit(spec, state)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_ejection_past_churn_limit_scaled(spec, state):
|
||||
assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
yield from run_test_ejection_past_churn_limit(spec, state)
|
||||
|
||||
|
||||
def run_test_activation_queue_activation_and_ejection(spec, state, num_per_status):
|
||||
# move past first two irregular epochs wrt finality
|
||||
next_epoch(spec, state)
|
||||
next_epoch(spec, state)
|
||||
|
||||
# ready for entrance into activation queue
|
||||
activation_queue_index = 0
|
||||
mock_deposit(spec, state, activation_queue_index)
|
||||
activation_queue_start_index = 0
|
||||
activation_queue_indices = list(range(activation_queue_start_index, activation_queue_start_index + num_per_status))
|
||||
for validator_index in activation_queue_indices:
|
||||
mock_deposit(spec, state, validator_index)
|
||||
|
||||
# ready for activation
|
||||
activation_index = 1
|
||||
mock_deposit(spec, state, activation_index)
|
||||
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
|
||||
state.validators[activation_index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
|
||||
activation_start_index = num_per_status
|
||||
activation_indices = list(range(activation_start_index, activation_start_index + num_per_status))
|
||||
for validator_index in activation_indices:
|
||||
mock_deposit(spec, state, validator_index)
|
||||
state.validators[validator_index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
|
||||
|
||||
# ready for ejection
|
||||
ejection_index = 2
|
||||
state.validators[ejection_index].effective_balance = spec.config.EJECTION_BALANCE
|
||||
ejection_start_index = num_per_status * 2
|
||||
ejection_indices = list(range(ejection_start_index, ejection_start_index + num_per_status))
|
||||
for validator_index in ejection_indices:
|
||||
state.validators[validator_index].effective_balance = spec.config.EJECTION_BALANCE
|
||||
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
yield from run_process_registry_updates(spec, state)
|
||||
|
||||
# validator moved into activation queue
|
||||
validator = state.validators[activation_queue_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
# all eligible validators moved into activation queue
|
||||
for validator_index in activation_queue_indices:
|
||||
validator = state.validators[validator_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
|
||||
# validator activated for future epoch
|
||||
validator = state.validators[activation_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
# up to churn limit validators get activated for future epoch from the queue
|
||||
for validator_index in activation_indices[:churn_limit]:
|
||||
validator = state.validators[validator_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
|
||||
# validator ejected for future epoch
|
||||
validator = state.validators[ejection_index]
|
||||
assert validator.exit_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert not spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
|
||||
)
|
||||
# any remaining validators do not exit the activation queue
|
||||
for validator_index in activation_indices[churn_limit:]:
|
||||
validator = state.validators[validator_index]
|
||||
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# all ejection balance validators ejected for a future epoch
|
||||
for i, validator_index in enumerate(ejection_indices):
|
||||
validator = state.validators[validator_index]
|
||||
assert validator.exit_epoch != spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
queue_offset = i // churn_limit
|
||||
assert not spec.is_active_validator(
|
||||
validator,
|
||||
spec.compute_activation_exit_epoch(spec.get_current_epoch(state)) + queue_offset
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_activation_and_ejection__1(spec, state):
|
||||
yield from run_test_activation_queue_activation_and_ejection(spec, state, 1)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_activation_and_ejection__churn_limit(spec, state):
|
||||
num_validators_per_status= spec.get_validator_churn_limit(state)
|
||||
yield from run_test_activation_queue_activation_and_ejection(spec, state, num_validators_per_status)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_activation_queue_activation_and_ejection__exceed_churn_limit(spec, state):
|
||||
num_validators_per_status = spec.get_validator_churn_limit(state) + 1
|
||||
yield from run_test_activation_queue_activation_and_ejection(spec, state, num_validators_per_status)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
assert churn_limit > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
yield from run_test_activation_queue_activation_and_ejection(spec, state, churn_limit)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_activation_queue_activation_and_ejection__exceed_scaled_churn_limit(spec, state):
|
||||
churn_limit = spec.get_validator_churn_limit(state)
|
||||
assert churn_limit > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
|
||||
num_validators_per_status = churn_limit * 2
|
||||
yield from run_test_activation_queue_activation_and_ejection(spec, state, num_validators_per_status)
|
||||
|
|
Loading…
Reference in New Issue