From 69f2a3140639d036f7c9766b758e26c7de87afe5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 26 May 2021 00:13:33 +0800 Subject: [PATCH 1/3] Add some config invariant checks --- setup.py | 20 -------- .../test/altair/unittests/__init__.py | 0 .../unittests/test_config_invariants.py | 17 +++++++ .../pyspec/eth2spec/test/helpers/constants.py | 6 +++ .../test/phase0/unittests/__init__.py | 0 .../unittests/test_config_invariants.py | 49 +++++++++++++++++++ .../phase0/unittests/validator/__init__.py | 0 7 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/altair/unittests/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py create mode 100644 tests/core/pyspec/eth2spec/test/phase0/unittests/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py create mode 100644 tests/core/pyspec/eth2spec/test/phase0/unittests/validator/__init__.py diff --git a/setup.py b/setup.py index 99e501d9f..e87bc7e4a 100644 --- a/setup.py +++ b/setup.py @@ -297,14 +297,6 @@ class SpecBuilder(ABC): """ raise NotImplementedError() - @classmethod - @abstractmethod - def invariant_checks(cls) -> str: - """ - The invariant checks - """ - raise NotImplementedError() - @classmethod @abstractmethod def build_spec(cls, preset_name: str, @@ -425,10 +417,6 @@ get_attesting_indices = cache_this( def hardcoded_custom_type_dep_constants(cls) -> Dict[str, str]: return {} - @classmethod - def invariant_checks(cls) -> str: - return '' - @classmethod def build_spec(cls, preset_name: str, source_files: Sequence[Path], preset_files: Sequence[Path], config_file: Path) -> str: @@ -475,13 +463,6 @@ def get_generalized_index(ssz_class: Any, *path: Sequence[Union[int, SSZVariable } return {**super().hardcoded_ssz_dep_constants(), **constants} - @classmethod - def invariant_checks(cls) -> str: - return ''' -assert ( - TIMELY_HEAD_WEIGHT + TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + SYNC_REWARD_WEIGHT + PROPOSER_WEIGHT -) == WEIGHT_DENOMINATOR''' - # # MergeSpecBuilder @@ -647,7 +628,6 @@ def objects_to_spec(preset_name: str, # Since some constants are hardcoded in setup.py, the following assertions verify that the hardcoded constants are # as same as the spec definition. + ('\n\n\n' + ssz_dep_constants_verification if ssz_dep_constants_verification != '' else '') - + ('\n' + builder.invariant_checks() if builder.invariant_checks() != '' else '') + '\n' ) return spec diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/__init__.py b/tests/core/pyspec/eth2spec/test/altair/unittests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py new file mode 100644 index 000000000..d24b56adc --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py @@ -0,0 +1,17 @@ +from eth2spec.test.context import ( + spec_state_test, + with_phases, +) +from eth2spec.test.helpers.constants import ALTAIR + + +@with_phases([ALTAIR]) +@spec_state_test +def test_weight_denominator(spec, state): + assert ( + spec.TIMELY_HEAD_WEIGHT + + spec.TIMELY_SOURCE_WEIGHT + + spec.TIMELY_TARGET_WEIGHT + + spec.SYNC_REWARD_WEIGHT + + spec.PROPOSER_WEIGHT + ) == spec.WEIGHT_DENOMINATOR diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 4e98845c4..8f116dc3d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -32,3 +32,9 @@ MAINNET = PresetBaseName('mainnet') MINIMAL = PresetBaseName('minimal') ALL_PRESETS = (MINIMAL, MAINNET) + + +# +# Number +# +MAX_UINT_64 = 2**64 - 1 diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/__init__.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py new file mode 100644 index 000000000..fc73e66ac --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py @@ -0,0 +1,49 @@ +from eth2spec.test.context import ( + spec_state_test, + with_all_phases, + is_post_altair, +) +from eth2spec.test.helpers.constants import MAX_UINT_64 + + +def check_bound(value, lower_bound, upper_bound): + assert value >= lower_bound + assert value <= upper_bound + + +@with_all_phases +@spec_state_test +def test_validators(spec, state): + check_bound(spec.VALIDATOR_REGISTRY_LIMIT, 1, MAX_UINT_64) + check_bound(spec.MAX_COMMITTEES_PER_SLOT, 1, MAX_UINT_64) + check_bound(spec.TARGET_COMMITTEE_SIZE, 1, MAX_UINT_64) + + check_bound(spec.MAX_VALIDATORS_PER_COMMITTEE, 1, spec.VALIDATOR_REGISTRY_LIMIT) + check_bound(spec.config.MIN_PER_EPOCH_CHURN_LIMIT, 1, spec.VALIDATOR_REGISTRY_LIMIT) + check_bound(spec.config.CHURN_LIMIT_QUOTIENT, 1, spec.VALIDATOR_REGISTRY_LIMIT) + + check_bound(spec.config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, spec.TARGET_COMMITTEE_SIZE, MAX_UINT_64) + + +@with_all_phases +@spec_state_test +def test_hysteresis_quotient(spec, state): + check_bound(spec.HYSTERESIS_QUOTIENT, 1, MAX_UINT_64) + check_bound(spec.HYSTERESIS_DOWNWARD_MULTIPLIER, 1, spec.HYSTERESIS_QUOTIENT) + check_bound(spec.HYSTERESIS_UPWARD_MULTIPLIER, spec.HYSTERESIS_QUOTIENT, MAX_UINT_64) + + +@with_all_phases +@spec_state_test +def test_incentives(spec, state): + # Ensure no ETH is minted in slash_validator + if is_post_altair(spec): + assert spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR <= spec.WHISTLEBLOWER_REWARD_QUOTIENT + else: + assert spec.MIN_SLASHING_PENALTY_QUOTIENT <= spec.WHISTLEBLOWER_REWARD_QUOTIENT + + +@with_all_phases +@spec_state_test +def test_time(spec, state): + assert spec.SLOTS_PER_EPOCH <= spec.SLOTS_PER_HISTORICAL_ROOT diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/__init__.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/validator/__init__.py new file mode 100644 index 000000000..e69de29bb From b5f9b5d74f795ebb0d6d420166ff796f15e5e504 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 26 May 2021 01:21:04 +0800 Subject: [PATCH 2/3] Add more checks --- .../test/altair/unittests/test_config_invariants.py | 6 ++++++ .../test/phase0/unittests/test_config_invariants.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py index d24b56adc..4443f97e0 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py @@ -15,3 +15,9 @@ def test_weight_denominator(spec, state): + spec.SYNC_REWARD_WEIGHT + spec.PROPOSER_WEIGHT ) == spec.WEIGHT_DENOMINATOR + + +@with_phases([ALTAIR]) +@spec_state_test +def test_inactivity_score(spec, state): + assert spec.config.INACTIVITY_SCORE_BIAS <= spec.config.INACTIVITY_SCORE_RECOVERY_RATE diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py index fc73e66ac..078b48ea5 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py @@ -47,3 +47,9 @@ def test_incentives(spec, state): @spec_state_test def test_time(spec, state): assert spec.SLOTS_PER_EPOCH <= spec.SLOTS_PER_HISTORICAL_ROOT + + +@with_all_phases +@spec_state_test +def test_networking(spec, state): + assert spec.RANDOM_SUBNETS_PER_VALIDATOR <= spec.ATTESTATION_SUBNET_COUNT From 7710d4fa5c87d2f8e7b1e32f77b15b08851720c8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 7 Jun 2021 07:55:04 -0600 Subject: [PATCH 3/3] add a few more preset/config invariants --- .../unittests/test_config_invariants.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py index 078b48ea5..b39b011b4 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py @@ -18,13 +18,28 @@ def test_validators(spec, state): check_bound(spec.MAX_COMMITTEES_PER_SLOT, 1, MAX_UINT_64) check_bound(spec.TARGET_COMMITTEE_SIZE, 1, MAX_UINT_64) - check_bound(spec.MAX_VALIDATORS_PER_COMMITTEE, 1, spec.VALIDATOR_REGISTRY_LIMIT) + # Note: can be less if you assume stricters bounds on validator set based on total ETH supply + maximum_validators_per_committee = ( + spec.VALIDATOR_REGISTRY_LIMIT + // spec.SLOTS_PER_EPOCH + // spec.MAX_COMMITTEES_PER_SLOT + ) + check_bound(spec.MAX_VALIDATORS_PER_COMMITTEE, 1, maximum_validators_per_committee) check_bound(spec.config.MIN_PER_EPOCH_CHURN_LIMIT, 1, spec.VALIDATOR_REGISTRY_LIMIT) check_bound(spec.config.CHURN_LIMIT_QUOTIENT, 1, spec.VALIDATOR_REGISTRY_LIMIT) check_bound(spec.config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, spec.TARGET_COMMITTEE_SIZE, MAX_UINT_64) +@with_all_phases +@spec_state_test +def test_balances(spec, state): + assert spec.MAX_EFFECTIVE_BALANCE % spec.EFFECTIVE_BALANCE_INCREMENT == 0 + check_bound(spec.MIN_DEPOSIT_AMOUNT, 1, MAX_UINT_64) + check_bound(spec.MAX_EFFECTIVE_BALANCE, spec.MIN_DEPOSIT_AMOUNT, MAX_UINT_64) + check_bound(spec.MAX_EFFECTIVE_BALANCE, spec.EFFECTIVE_BALANCE_INCREMENT, MAX_UINT_64) + + @with_all_phases @spec_state_test def test_hysteresis_quotient(spec, state): @@ -47,6 +62,10 @@ def test_incentives(spec, state): @spec_state_test def test_time(spec, state): assert spec.SLOTS_PER_EPOCH <= spec.SLOTS_PER_HISTORICAL_ROOT + assert spec.MIN_SEED_LOOKAHEAD < spec.MAX_SEED_LOOKAHEAD + assert spec.SLOTS_PER_HISTORICAL_ROOT % spec.SLOTS_PER_EPOCH == 0 + check_bound(spec.SLOTS_PER_HISTORICAL_ROOT, spec.SLOTS_PER_EPOCH, MAX_UINT_64) + check_bound(spec.MIN_ATTESTATION_INCLUSION_DELAY, 1, spec.SLOTS_PER_EPOCH) @with_all_phases