Add basic activation churn limit tests

This commit is contained in:
Hsiao-Wei Wang 2023-09-13 17:04:39 +08:00
parent 298a6304de
commit 417b95c3e6
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
9 changed files with 129 additions and 13 deletions

View File

@ -151,4 +151,4 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 256
WHISK_PROPOSER_SELECTION_GAP: 2
# EIP7668
MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 12

View File

@ -85,8 +85,8 @@ INACTIVITY_SCORE_BIAS: 4
INACTIVITY_SCORE_RECOVERY_RATE: 16
# 2**4 * 10**9 (= 16,000,000,000) Gwei
EJECTION_BALANCE: 16000000000
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# [customized]
MIN_PER_EPOCH_CHURN_LIMIT: 2
# [customized] scale queue churn at much lower validator counts for testing
CHURN_LIMIT_QUOTIENT: 32
@ -150,4 +150,5 @@ WHISK_EPOCHS_PER_SHUFFLING_PHASE: 4
WHISK_PROPOSER_SELECTION_GAP: 1
# EIP7668
MAX_PER_EPOCH_INBOUND_CHURN_LIMIT: 12
# [customized]
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4

View File

@ -44,7 +44,7 @@ def get_validator_activation_churn_limit(state: BeaconState) -> uint64:
"""
Return the validator activation churn limit for the current epoch.
"""
return min(MAX_PER_EPOCH_INBOUND_CHURN_LIMIT, get_validator_churn_limit(state))
return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state))
```
## Beacon chain state transition function

View File

@ -165,14 +165,34 @@ def default_balances(spec: Spec):
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances(spec: Spec):
def scaled_churn_balances_min_churn_limit(spec: 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, ...)`
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (2 + spec.config.MIN_PER_EPOCH_CHURN_LIMIT)
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MIN_PER_EPOCH_CHURN_LIMIT + 2)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances_equal_inbound_churn_limit(spec: 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)
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances_exceed_inbound_churn_limit(spec: 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)
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + 2)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
@ -548,6 +568,7 @@ with_capella_and_later = with_all_phases_from(CAPELLA)
with_deneb_and_later = with_all_phases_from(DENEB)
with_eip6110_and_later = with_all_phases_from(EIP6110)
with_eip7002_and_later = with_all_phases_from(EIP7002)
with_eip7668_and_later = with_all_phases_from(EIP7668)
class quoted_str(str):

View File

@ -0,0 +1,86 @@
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_eip7668_and_later,
spec_test,
spec_state_test,
single_phase,
with_custom_state,
with_presets,
scaled_churn_balances_exceed_inbound_churn_limit,
scaled_churn_balances_equal_inbound_churn_limit,
)
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
def run_process_registry_updates(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_registry_updates')
def run_test_inbound_churn_limit(spec, state):
mock_activations = 1
for i in range(mock_activations):
index = len(state.validators) + i
validator = spec.Validator(
pubkey=pubkeys[index],
withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20,
activation_eligibility_epoch=0,
activation_epoch=spec.FAR_FUTURE_EPOCH,
exit_epoch=spec.FAR_FUTURE_EPOCH,
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
effective_balance=spec.MAX_EFFECTIVE_BALANCE,
)
state.validators.append(validator)
state.balances.append(spec.MAX_EFFECTIVE_BALANCE)
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(0)
state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH
churn_limit_0 = spec.get_validator_activation_churn_limit(state)
yield from run_process_registry_updates(spec, state)
# Half should churn in first run of registry update
for i in range(mock_activations):
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
@with_eip7668_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances_exceed_inbound_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_inbound_churn_limit__greater_than_inbound_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) > spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_inbound_churn_limit(spec, state)
@with_eip7668_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances_equal_inbound_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_inbound_churn_limit__equal_to_inbound_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_inbound_churn_limit(spec, state)
@with_eip7668_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_state_test
def test_inbound_churn_limit__less_than_inbound_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_inbound_churn_limit(spec, state)

View File

@ -46,3 +46,7 @@ def is_post_eip6110(spec):
def is_post_eip7002(spec):
return is_post_fork(spec.fork, EIP7002)
def is_post_eip7668(spec):
return is_post_fork(spec.fork, EIP7668)

View File

@ -5,7 +5,7 @@ from eth2spec.test.context import (
spec_test, spec_state_test,
with_all_phases, single_phase,
with_custom_state, with_presets,
scaled_churn_balances,
scaled_churn_balances_min_churn_limit,
)
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
@ -164,7 +164,8 @@ def test_activation_queue_efficiency_min(spec, state):
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
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
@ -227,7 +228,8 @@ def test_ejection_past_churn_limit_min(spec, state):
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
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
@ -324,7 +326,8 @@ def test_activation_queue_activation_and_ejection__exceed_churn_limit(spec, stat
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
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)
@ -336,7 +339,8 @@ def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, stat
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
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)