Merge pull request #3656 from ralexstokes/fix-7251-tests

Fix EIP-7251 tests
This commit is contained in:
Alex Stokes 2024-04-15 19:21:46 -06:00 committed by GitHub
commit 77ec547cc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1951 additions and 1115 deletions

View File

@ -168,6 +168,19 @@ jobs:
command: make citest fork=electra
- store_test_results:
path: tests/core/pyspec/test-reports
test-eip7251:
docker:
- image: circleci/python:3.9
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_pyspec_cached_venv
- run:
name: Run py-tests
command: make citest fork=eip7251
- store_test_results:
path: tests/core/pyspec/test-reports
test-whisk:
docker:
- image: circleci/python:3.9
@ -317,6 +330,9 @@ workflows:
- test-electra:
requires:
- install_pyspec_test
- test-eip7251:
requires:
- install_pyspec_test
- test-whisk:
requires:
- install_pyspec_test

View File

@ -71,7 +71,7 @@ jobs:
needs: [preclear,lint,codespell,table_of_contents]
strategy:
matrix:
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "whisk", "eip7594"]
version: ["phase0", "altair", "bellatrix", "capella", "deneb", "electra", "eip7251", "whisk", "eip7594"]
steps:
- name: Checkout this repo
uses: actions/checkout@v3.2.0

View File

@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \
$(wildcard $(SPEC_DIR)/_features/*/*/*.md) \
$(wildcard $(SSZ_DIR)/*.md)
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk
ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra eip7251 whisk
# The parameters for commands. Use `foreach` to avoid listing specs again.
COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S)

View File

@ -509,7 +509,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None:
validator = state.validators[index]
if has_eth1_withdrawal_credential(validator):
validator.withdrawal_credentials[:1] = COMPOUNDING_WITHDRAWAL_PREFIX
validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
queue_excess_active_balance(state, index)
```
@ -1055,7 +1055,7 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu
assert get_current_epoch(state) >= voluntary_exit.epoch
# Verify the validator has been active long enough
assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
# Only exit validator if it has no pending withdrawals in the queue
# [New in EIP-7251] Only exit validator if it has no pending withdrawals in the queue
assert get_pending_balance_to_withdraw(state, voluntary_exit.validator_index) == 0 # [New in EIP7251]
# Verify signature
domain = compute_domain(DOMAIN_VOLUNTARY_EXIT, CAPELLA_FORK_VERSION, state.genesis_validators_root)

View File

@ -2,6 +2,7 @@ from eth2spec.test.context import (
spec_state_test,
with_capella_and_later,
)
from eth2spec.test.helpers.forks import is_post_eip7251
from eth2spec.test.helpers.state import next_epoch_via_block
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
@ -30,10 +31,25 @@ def test_success_top_up_to_withdrawn_validator(spec, state):
yield from run_deposit_processing(spec, state, deposit, validator_index)
assert state.balances[validator_index] == amount
assert state.validators[validator_index].effective_balance == 0
if is_post_eip7251(spec):
pending_balance_deposits_len = len(state.pending_balance_deposits)
pending_balance_deposit = state.pending_balance_deposits[pending_balance_deposits_len - 1]
assert pending_balance_deposit.amount == amount
assert pending_balance_deposit.index == validator_index
else:
assert state.balances[validator_index] == amount
assert state.validators[validator_index].effective_balance == 0
validator = state.validators[validator_index]
balance = state.balances[validator_index]
current_epoch = spec.get_current_epoch(state)
assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch)
if is_post_eip7251(spec):
has_execution_withdrawal = spec.has_execution_withdrawal_credential(validator)
is_withdrawable = validator.withdrawable_epoch <= current_epoch
has_non_zero_balance = pending_balance_deposit.amount > 0
# NOTE: directly compute `is_fully_withdrawable_validator` conditions here
# to work around how the epoch processing changed balance updates
assert has_execution_withdrawal and is_withdrawable and has_non_zero_balance
else:
assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch)

View File

@ -19,6 +19,7 @@ from eth2spec.test.helpers.state import (
next_slot,
)
from eth2spec.test.helpers.withdrawals import (
get_expected_withdrawals,
prepare_expected_withdrawals,
set_eth1_withdrawal_credential_with_balance,
set_validator_fully_withdrawable,
@ -62,7 +63,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
expected_withdrawals = spec.get_expected_withdrawals(state)
expected_withdrawals = get_expected_withdrawals(spec, state)
assert len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD
if num_expected_withdrawals is not None:
assert len(expected_withdrawals) == num_expected_withdrawals
@ -87,7 +88,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with
assert state.next_withdrawal_validator_index == next_withdrawal_validator_index % len(state.validators)
elif len(expected_withdrawals) <= spec.MAX_WITHDRAWALS_PER_PAYLOAD:
bound = min(spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
assert len(spec.get_expected_withdrawals(state)) <= bound
assert len(get_expected_withdrawals(spec, state)) <= bound
elif len(expected_withdrawals) > spec.MAX_WITHDRAWALS_PER_PAYLOAD:
raise ValueError('len(expected_withdrawals) should not be greater than MAX_WITHDRAWALS_PER_PAYLOAD')
@ -100,7 +101,7 @@ def run_withdrawals_processing(spec, state, execution_payload, num_expected_with
@with_capella_and_later
@spec_state_test
def test_success_zero_expected_withdrawals(spec, state):
assert len(spec.get_expected_withdrawals(state)) == 0
assert len(get_expected_withdrawals(spec, state)) == 0
next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)

View File

@ -1,4 +1,5 @@
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.helpers.forks import is_post_eip7251
from eth2spec.test.context import (
with_capella_and_later,
spec_state_test,
@ -20,6 +21,7 @@ from eth2spec.test.helpers.attestations import (
next_epoch_with_attestations,
)
from eth2spec.test.helpers.withdrawals import (
get_expected_withdrawals,
set_eth1_withdrawal_credential_with_balance,
set_validator_fully_withdrawable,
set_validator_partially_withdrawable,
@ -202,7 +204,7 @@ def test_full_withdrawal_in_epoch_transition(spec, state):
index = 0
current_epoch = spec.get_current_epoch(state)
set_validator_fully_withdrawable(spec, state, index, current_epoch)
assert len(spec.get_expected_withdrawals(state)) == 1
assert len(get_expected_withdrawals(spec, state)) == 1
yield 'pre', state
@ -214,7 +216,7 @@ def test_full_withdrawal_in_epoch_transition(spec, state):
yield 'post', state
assert state.balances[index] == 0
assert len(spec.get_expected_withdrawals(state)) == 0
assert len(get_expected_withdrawals(spec, state)) == 0
@with_capella_and_later
@ -224,7 +226,7 @@ def test_partial_withdrawal_in_epoch_transition(spec, state):
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
pre_balance = state.balances[index]
assert len(spec.get_expected_withdrawals(state)) == 1
assert len(get_expected_withdrawals(spec, state)) == 1
yield 'pre', state
@ -238,7 +240,7 @@ def test_partial_withdrawal_in_epoch_transition(spec, state):
assert state.balances[index] < pre_balance
# Potentially less than due to sync committee penalty
assert state.balances[index] <= spec.MAX_EFFECTIVE_BALANCE
assert len(spec.get_expected_withdrawals(state)) == 0
assert len(get_expected_withdrawals(spec, state)) == 0
@with_capella_and_later
@ -250,7 +252,7 @@ def test_many_partial_withdrawals_in_epoch_transition(spec, state):
index = (i + state.next_withdrawal_index) % len(state.validators)
set_validator_partially_withdrawable(spec, state, index, excess_balance=1000000000000)
assert len(spec.get_expected_withdrawals(state)) == spec.MAX_WITHDRAWALS_PER_PAYLOAD
assert (len(get_expected_withdrawals(spec, state)) == spec.MAX_WITHDRAWALS_PER_PAYLOAD)
yield 'pre', state
@ -261,7 +263,7 @@ def test_many_partial_withdrawals_in_epoch_transition(spec, state):
yield 'blocks', [signed_block]
yield 'post', state
assert len(spec.get_expected_withdrawals(state)) == 1
assert len(get_expected_withdrawals(spec, state)) == 1
def _perform_valid_withdrawal(spec, state):
@ -272,7 +274,7 @@ def _perform_valid_withdrawal(spec, state):
next_slot(spec, state)
pre_next_withdrawal_index = state.next_withdrawal_index
expected_withdrawals = spec.get_expected_withdrawals(state)
expected_withdrawals = get_expected_withdrawals(spec, state)
pre_state = state.copy()
@ -357,7 +359,11 @@ def test_top_up_and_partial_withdrawable_validator(spec, state):
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [signed_block]
# ensure we go through an epoch transition, to account for post-EIP-7251 behavior
block_in_next_epoch = build_empty_block(spec, state, slot=state.slot + spec.SLOTS_PER_EPOCH)
signed_block_in_next_epoch = state_transition_and_sign_block(spec, state, block_in_next_epoch)
yield 'blocks', [signed_block, signed_block_in_next_epoch]
yield 'post', state
# Since withdrawals happen before deposits, it becomes partially withdrawable after state transition.
@ -395,9 +401,13 @@ def test_top_up_to_fully_withdrawn_validator(spec, state):
signed_block_1 = state_transition_and_sign_block(spec, state, block)
balance = state.balances[validator_index]
if is_post_eip7251(spec):
balance += state.pending_balance_deposits[0].amount
assert spec.is_fully_withdrawable_validator(
state.validators[validator_index],
state.balances[validator_index],
balance,
spec.get_current_epoch(state)
)

View File

@ -1,4 +1,5 @@
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.forks import is_post_eip7251
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_deneb_and_later,
@ -47,7 +48,9 @@ def run_test_activation_churn_limit(spec, state):
# Half should churn in first run of registry update
for i in range(mock_activations):
index = validator_count_0 + i
if index < validator_count_0 + churn_limit_0:
# NOTE: activations are gated different after EIP-7251
# all eligible validators have been activated
if index < validator_count_0 + churn_limit_0 or is_post_eip7251(spec):
# The eligible validators within the activation churn limit should have been activated
assert state.validators[index].activation_epoch < spec.FAR_FUTURE_EPOCH
else:

View File

@ -4,7 +4,8 @@ from eth2spec.test.context import (
with_eip7251_and_later,
with_presets,
always_bls,
spec_test, single_phase,
spec_test,
single_phase,
with_custom_state,
scaled_churn_balances_exceed_activation_exit_churn_limit,
default_activation_threshold,
@ -27,15 +28,13 @@ from eth2spec.test.helpers.withdrawals import (
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation(spec, state):
print(spec.config.PRESET_BASE)
def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
@ -46,17 +45,30 @@ def test_basic_consolidation(spec, state):
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@ -64,7 +76,154 @@ def test_basic_consolidation(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_in_new_consolidation_epoch(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
# Set consolidation balance to consume to some arbitrary nonzero value below the churn limit
state.consolidation_balance_to_consume = spec.EFFECTIVE_BALANCE_INCREMENT
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
# consolidation_balance_to_consume is replenished to the churn limit since we move to a new consolidation epoch
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epochs
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the expected exit epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_consolidation_epoch = expected_exit_epoch
# Set some nonzero preexisting churn lower than churn limit and sufficient to process the consolidation
preexisting_churn = 2 * spec.MIN_ACTIVATION_BALANCE
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, signed_consolidation)
# Check consolidation churn is decremented correctly
assert (
state.consolidation_balance_to_consume
== preexisting_churn - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_insufficient_preexisting_churn(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# Set earliest consolidation epoch to the first available epoch
state.earliest_consolidation_epoch = spec.compute_activation_exit_epoch(
current_epoch
)
# Set preexisting churn lower than required to process the consolidation
preexisting_churn = spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.consolidation_balance_to_consume = preexisting_churn
yield from run_consolidation_processing(spec, state, signed_consolidation)
# It takes one more epoch to process the consolidation due to insufficient churn
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = spec.MIN_ACTIVATION_BALANCE % preexisting_churn
assert (
state.consolidation_balance_to_consume == consolidation_churn_limit - remainder
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_basic_consolidation_with_compounding_credential(spec, state):
@ -82,17 +241,23 @@ def test_basic_consolidation_with_compounding_credential(spec, state):
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@ -100,19 +265,21 @@ def test_basic_consolidation_with_compounding_credential(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_churn_limit_balance(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set source balance to consolidation churn limit
state.balances[source_index] = consolidation_churn_limit
source_validator = state.validators[source_index]
source_validator.effective_balance = consolidation_churn_limit
# Churn limit increases due to higher total balance
updated_consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
@ -121,17 +288,24 @@ def test_consolidation_churn_limit_balance(spec, state):
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
# validator's effective balance fits into the churn, exit as soon as possible
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == 0
assert (
state.consolidation_balance_to_consume
== updated_consolidation_churn_limit - consolidation_churn_limit
)
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@ -139,19 +313,19 @@ def test_consolidation_churn_limit_balance(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_larger_than_churn_limit(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set source balance higher than consolidation churn limit
state.balances[source_index] = consolidation_churn_limit + 1
state.validators[source_index].effective_balance = 2 * consolidation_churn_limit
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey]
target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey]
@ -160,17 +334,25 @@ def test_consolidation_balance_larger_than_churn_limit(spec, state):
set_compounding_withdrawal_credential(spec, state, source_index)
set_compounding_withdrawal_credential(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
# Consolidation churn limit increases due to higher total balance
new_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % new_churn_limit
expected_balance = new_churn_limit - remainder
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 1
# Check consolidation churn is decremented correctly
assert state.consolidation_balance_to_consume == consolidation_churn_limit - 1
assert state.consolidation_balance_to_consume == expected_balance
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@ -178,14 +360,14 @@ def test_consolidation_balance_larger_than_churn_limit(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_consolidation_balance_twice_the_churn_limit(spec, state):
def test_consolidation_balance_through_two_churn_epochs(spec, state):
# This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn
consolidation_churn_limit = spec.get_consolidation_churn_limit(state)
# Set the consolidation balance to consume equal to churn limit
state.consolidation_balance_to_consume = consolidation_churn_limit
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
@ -198,27 +380,36 @@ def test_consolidation_balance_twice_the_churn_limit(spec, state):
set_compounding_withdrawal_credential(spec, state, target_index)
# Set source balance higher than consolidation churn limit
state.balances[source_index] = 2 * consolidation_churn_limit
state.validators[source_index].effective_balance = 3 * consolidation_churn_limit
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
new_churn_limit = spec.get_consolidation_churn_limit(state)
remainder = state.validators[source_index].effective_balance % new_churn_limit
expected_balance = new_churn_limit - remainder
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(spec, state, signed_consolidation)
# when exiting a multiple of the churn limit greater than 1, an extra exit epoch is added
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + 2
assert state.validators[0].exit_epoch == expected_exit_epoch
# since the earliest exit epoch moves to a new one, consolidation balance is back to full
assert state.consolidation_balance_to_consume == consolidation_churn_limit
assert state.consolidation_balance_to_consume == expected_balance
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_multiple_consolidations_below_churn(spec, state):
@ -239,12 +430,17 @@ def test_multiple_consolidations_below_churn(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index,
),
source_privkey,
target_privkey,
)
consolidations.append(signed_consolidation)
# Now run all the consolidations
@ -257,7 +453,10 @@ def test_multiple_consolidations_below_churn(spec, state):
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
assert state.earliest_consolidation_epoch == expected_exit_epoch
assert state.consolidation_balance_to_consume == consolidation_churn_limit - 3 * spec.MIN_ACTIVATION_BALANCE
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - 3 * spec.MIN_ACTIVATION_BALANCE
)
for i in range(3):
assert state.validators[2 * i].exit_epoch == expected_exit_epoch
@ -265,7 +464,9 @@ def test_multiple_consolidations_below_churn(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_multiple_consolidations_equal_churn(spec, state):
@ -286,12 +487,17 @@ def test_multiple_consolidations_equal_churn(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index,
),
source_privkey,
target_privkey,
)
consolidations.append(signed_consolidation)
# Now run all the consolidations
@ -312,7 +518,9 @@ def test_multiple_consolidations_equal_churn(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_multiple_consolidations_above_churn(spec, state):
@ -332,12 +540,17 @@ def test_multiple_consolidations_above_churn(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index,
),
source_privkey,
target_privkey,
)
consolidations.append(signed_consolidation)
# Now run all the consolidations
@ -356,19 +569,25 @@ def test_multiple_consolidations_above_churn(spec, state):
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch, source_index=source_index, target_index=target_index
),
source_privkey,
target_privkey,
)
# This is the interesting part of the test: on a pre-state with full consolidation queue,
# when processing an additional consolidation, it results in an exit in a later epoch
yield from run_consolidation_processing(spec, state, signed_consolidation)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
assert state.earliest_consolidation_epoch == expected_exit_epoch + 1
assert state.consolidation_balance_to_consume == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
assert (
state.consolidation_balance_to_consume
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
)
assert state.validators[source_index].exit_epoch == expected_exit_epoch + 1
for i in range(4):
assert state.validators[2 * i].exit_epoch == expected_exit_epoch
@ -377,7 +596,9 @@ def test_multiple_consolidations_above_churn(spec, state):
@with_eip7251_and_later
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
@with_custom_state(
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, threshold_fn=default_activation_threshold)
balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit,
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
def test_multiple_consolidations_equal_twice_churn(spec, state):
@ -398,12 +619,17 @@ def test_multiple_consolidations_equal_twice_churn(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=source_index,
target_index=target_index,
),
source_privkey,
target_privkey,
)
consolidations.append(signed_consolidation)
# Now run all the consolidations
@ -425,6 +651,7 @@ def test_multiple_consolidations_equal_twice_churn(spec, state):
# Failing tests
@with_eip7251_and_later
@spec_state_test
def test_invalid_source_equals_target(spec, state):
@ -435,31 +662,69 @@ def test_invalid_source_equals_target(spec, state):
# Set withdrawal credentials to eth1
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=validator_index,
target_index=validator_index),
validator_privkey, validator_privkey)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(
epoch=current_epoch,
source_index=validator_index,
target_index=validator_index,
),
validator_privkey,
validator_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@spec_state_test
def test_invalid_exceed_pending_consolidations_limit(spec, state):
state.pending_consolidations = (
[spec.PendingConsolidation(source_index=0, target_index=1)] * spec.PENDING_CONSOLIDATIONS_LIMIT
)
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
] * spec.PENDING_CONSOLIDATIONS_LIMIT
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey, target_privkey)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@spec_state_test
def test_invalid_not_enough_consolidation_churn_available(spec, state):
state.validators = state.validators[0:2]
state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
]
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -470,12 +735,18 @@ def test_invalid_exited_source(spec, state):
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# exit source
spec.initiate_validator_exit(state, 0)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -487,15 +758,18 @@ def test_invalid_exited_target(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# exit target
spec.initiate_validator_exit(state, 1)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -506,15 +780,18 @@ def test_invalid_inactive_source(spec, state):
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# set source validator as not yet activated
state.validators[0].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -526,15 +803,18 @@ def test_invalid_inactive_target(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# set target validator as not yet activated
state.validators[1].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -543,13 +823,16 @@ def test_invalid_no_execution_withdrawal_credential(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -558,16 +841,19 @@ def test_invalid_different_credentials(spec, state):
current_epoch = spec.get_current_epoch(state)
source_privkey = pubkey_to_privkey[state.validators[0].pubkey]
target_privkey = pubkey_to_privkey[state.validators[1].pubkey]
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Set source and target withdrawal credentials to different eth1 credentials
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1, address=b'\x10' * 20)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
set_eth1_withdrawal_credential_with_balance(spec, state, 1, address=b"\x10" * 20)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -580,15 +866,18 @@ def test_invalid_source_signature(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Change the pubkey of the source validator, invalidating its signature
state.validators[0].pubkey = state.validators[1].pubkey
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -601,15 +890,18 @@ def test_invalid_target_signature(spec, state):
# Set source and target withdrawal credentials to the same eth1 credential
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch,
source_index=0,
target_index=1),
source_privkey, target_privkey)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
# Change the pubkey of the target validator, invalidating its signature
state.validators[1].pubkey = state.validators[2].pubkey
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)
@with_eip7251_and_later
@ -622,10 +914,13 @@ def test_invalid_before_specified_epoch(spec, state):
set_eth1_withdrawal_credential_with_balance(spec, state, 0)
set_eth1_withdrawal_credential_with_balance(spec, state, 1)
# set epoch=current_epoch + 1, so it's too early to process it
signed_consolidation = sign_consolidation(spec, state,
spec.Consolidation(
epoch=current_epoch + 1,
source_index=0,
target_index=1),
source_privkey, target_privkey)
yield from run_consolidation_processing(spec, state, signed_consolidation, valid=False)
signed_consolidation = sign_consolidation(
spec,
state,
spec.Consolidation(epoch=current_epoch + 1, source_index=0, target_index=1),
source_privkey,
target_privkey,
)
yield from run_consolidation_processing(
spec, state, signed_consolidation, valid=False
)

View File

@ -1,284 +0,0 @@
from eth2spec.test.helpers.deposits import (
build_deposit,
prepare_state_and_deposit,
run_deposit_processing_eip7251,
run_deposit_processing_eip7251_with_specific_fork_version,
sign_deposit_data,
)
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
always_bls,
)
@with_eip7251_and_later
@spec_state_test
def test_new_deposit_under_min_activation_balance(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement.
amount = spec.MIN_ACTIVATION_BALANCE - 1
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_new_deposit_min(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MIN_DEPOSIT_AMOUNT
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_new_deposit_between_min_and_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE_EIP7251 // 2
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_new_deposit_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
# effective balance will be exactly the same as balance.
amount = spec.MAX_EFFECTIVE_BALANCE_EIP7251
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_new_deposit_over_max(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE_EIP7251 + 1
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
# @with_eip7251_and_later
# @spec_state_test
# def test_top_up__max_effective_balance(spec, state):
# validator_index = 0
# amount = spec.MAX_EFFECTIVE_BALANCE_EIP7251 // 4
# deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
# state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_EIP7251
# state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE_EIP7251
# yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
# assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE_EIP7251 + amount
# assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE_EIP7251
@with_eip7251_and_later
@spec_state_test
@always_bls
def test_correct_sig_but_forked_state(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# deposits will always be valid, regardless of the current fork
state.fork.current_version = spec.Version('0x1234abcd')
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_new_deposit(spec, state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validators)
amount = spec.MIN_ACTIVATION_BALANCE
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index, effective=False)
@with_eip7251_and_later
@spec_state_test
def test_top_up__max_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE
state.validators[validator_index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
@with_eip7251_and_later
@spec_state_test
def test_top_up__less_effective_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
initial_balance = spec.MAX_EFFECTIVE_BALANCE - 1000
initial_effective_balance = spec.MAX_EFFECTIVE_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_eip7251_and_later
@spec_state_test
def test_top_up__zero_balance(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
initial_balance = 0
initial_effective_balance = 0
state.balances[validator_index] = initial_balance
state.validators[validator_index].effective_balance = initial_effective_balance
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_eip7251_and_later
@spec_state_test
@always_bls
def test_incorrect_sig_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_incorrect_withdrawal_credentials_top_up(spec, state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(b"junk")[1:]
deposit = prepare_state_and_deposit(
spec,
state,
validator_index,
amount,
withdrawal_credentials=withdrawal_credentials
)
# inconsistent withdrawal credentials, in top-ups, are allowed!
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_invalid_wrong_deposit_for_deposit_count(spec, state):
deposit_data_leaves = [spec.DepositData() for _ in range(len(state.validators))]
# build root for deposit_1
index_1 = len(deposit_data_leaves)
pubkey_1 = pubkeys[index_1]
privkey_1 = privkeys[index_1]
_, _, deposit_data_leaves = build_deposit(
spec,
deposit_data_leaves,
pubkey_1,
privkey_1,
spec.MAX_EFFECTIVE_BALANCE,
withdrawal_credentials=b'\x00' * 32,
signed=True,
)
deposit_count_1 = len(deposit_data_leaves)
# build root for deposit_2
index_2 = len(deposit_data_leaves)
pubkey_2 = pubkeys[index_2]
privkey_2 = privkeys[index_2]
deposit_2, root_2, deposit_data_leaves = build_deposit(
spec,
deposit_data_leaves,
pubkey_2,
privkey_2,
spec.MAX_EFFECTIVE_BALANCE,
withdrawal_credentials=b'\x00' * 32,
signed=True,
)
# state has root for deposit_2 but is at deposit_count for deposit_1
state.eth1_data.deposit_root = root_2
state.eth1_data.deposit_count = deposit_count_1
yield from run_deposit_processing_eip7251(spec, state, deposit_2, index_2, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_invalid_bad_merkle_proof(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(spec, state, validator_index, amount)
# mess up merkle branch
deposit.proof[5] = spec.Bytes32()
sign_deposit_data(spec, deposit.data, privkeys[validator_index])
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_key_validate_invalid_subgroup(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# All-zero pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey = b'\x00' * 48
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
def test_key_validate_invalid_decompression(spec, state):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
# `deserialization_fails_infinity_with_true_b_flag` BLS G1 deserialization test case.
# This pubkey would not pass `bls.KeyValidate`, but `process_deposit` would not throw exception.
pubkey_hex = 'c01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
pubkey = bytes.fromhex(pubkey_hex)
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, pubkey=pubkey, signed=True)
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index)
@with_eip7251_and_later
@spec_state_test
@always_bls
def test_ineffective_deposit_with_bad_fork_version(spec, state):
yield from run_deposit_processing_eip7251_with_specific_fork_version(
spec,
state,
fork_version=spec.Version('0xAaBbCcDd'),
effective=False,
)

View File

@ -10,6 +10,7 @@ from eth2spec.test.helpers.state import (
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_compounding_withdrawal_credential,
)
@ -19,6 +20,7 @@ from eth2spec.test.helpers.withdrawals import (
# Modified tests from 7002. Just testing EL-triggered exits, not partial withdrawals
@with_eip7251_and_later
@spec_state_test
def test_basic_exit(spec, state):
@ -28,15 +30,78 @@ def test_basic_exit(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=0,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdraw_request_processing(spec, state, execution_layer_withdraw_request)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request
)
@with_eip7251_and_later
@spec_state_test
def test_basic_exit_with_compounding_credentials(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL], "need full partial withdrawal queue")
def test_basic_exit_with_full_partial_withdrawal_queue(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
# Fill the partial withdrawal queue to the max (with a different validator index)
partial_withdrawal = spec.PendingPartialWithdrawal(
index=1, amount=1, withdrawable_epoch=current_epoch
)
state.pending_partial_withdrawals = [
partial_withdrawal
] * spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
# Exit should still be processed
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Invalid tests
@with_eip7251_and_later
@ -48,13 +113,15 @@ def test_incorrect_source_address(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
incorrect_address = b'\x33' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
incorrect_address = b"\x33" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=incorrect_address,
validator_pubkey=validator_pubkey,
amount=0,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdraw_request_processing(
@ -71,8 +138,10 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
# Set incorrect prefix
state.validators[validator_index].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX
@ -81,7 +150,7 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=0,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdraw_request_processing(
@ -98,14 +167,16 @@ def test_on_exit_initiated_validator(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
# Initiate exit earlier
spec.initiate_validator_exit(state, validator_index)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=0,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_execution_layer_withdraw_request_processing(
@ -119,16 +190,19 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=0,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
assert spec.get_current_epoch(state) < (
state.validators[validator_index].activation_epoch + spec.config.SHARD_COMMITTEE_PERIOD
state.validators[validator_index].activation_epoch
+ spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_execution_layer_withdraw_request_processing(
@ -138,24 +212,533 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
# Partial withdrawals tests
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Set excess balance exactly to the requested amount
state.balances[validator_index] += amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request_higher_excess_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Set excess balance higher than requested amount
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request_lower_than_excess_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
excess_balance = spec.EFFECTIVE_BALANCE_INCREMENT
amount = 2 * excess_balance
# Set excess balance higher than requested amount
state.balances[validator_index] += excess_balance
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_pending_withdrawals(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
# Add pending withdrawals
partial_withdrawal = spec.PendingPartialWithdrawal(
index=validator_index, amount=amount, withdrawable_epoch=current_epoch
)
state.pending_partial_withdrawals = [partial_withdrawal] * 2
# Set balance so that the validator still has excess balance even with the pending withdrawals
state.balances[validator_index] += 3 * amount
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_pending_withdrawals_and_high_amount(
spec, state
):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.UINT64_MAX
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
# Add many pending withdrawals
partial_withdrawal = spec.PendingPartialWithdrawal(
index=validator_index,
amount=spec.EFFECTIVE_BALANCE_INCREMENT,
withdrawable_epoch=current_epoch,
)
state.pending_partial_withdrawals = [partial_withdrawal] * (
spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT - 1
)
# Set balance so that the validator still has excess balance even with the pending withdrawals
state.balances[validator_index] = spec.MAX_EFFECTIVE_BALANCE_EIP7251
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_high_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.MAX_EFFECTIVE_BALANCE_EIP7251
state.balances[validator_index] = 3 * spec.MAX_EFFECTIVE_BALANCE_EIP7251
state.validators[validator_index].effective_balance = (
spec.MAX_EFFECTIVE_BALANCE_EIP7251
)
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
churn_limit = spec.get_activation_exit_churn_limit(state)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
exit_epoch = (
spec.compute_activation_exit_epoch(current_epoch) + amount // churn_limit
)
assert state.earliest_exit_epoch == exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_high_amount(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
# Set high amount requested to withdraw
amount = spec.UINT64_MAX
# Give the validator some excess balance to withdraw
state.balances[validator_index] += 1
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_low_amount(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = 1
# Give the validator some excess balance to withdraw
state.balances[validator_index] += amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
)
# Check that the assigned exit epoch is correct
assert state.earliest_exit_epoch == spec.compute_activation_exit_epoch(
current_epoch
)
# No-op partial withdrawal tests
@with_eip7251_and_later
@spec_state_test
@with_presets([MINIMAL], "need full partial withdrawal queue")
def test_partial_withdrawal_queue_full(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b'\x22' * 20
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, address=address)
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Ensure that the validator has sufficient excess balance
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=10 ** 9,
amount=amount,
)
# Fill the partial withdrawal queue to the max
partial_withdrawal = spec.PendingPartialWithdrawal(
index=1, amount=1, withdrawable_epoch=current_epoch
)
state.pending_partial_withdrawals = [
partial_withdrawal
] * spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_no_compounding_credentials(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Ensure that the validator has sufficient excess balance
state.balances[validator_index] += 2 * amount
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
success=False,
)
@with_eip7251_and_later
@spec_state_test
def test_no_excess_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_pending_withdrawals_consume_all_excess_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Add excess balance
state.balances[validator_index] += 10 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
# Add pending withdrawals totalling an amount equal to the excess balance
partial_withdrawal = spec.PendingPartialWithdrawal(
index=validator_index, amount=amount, withdrawable_epoch=current_epoch
)
state.pending_partial_withdrawals = [partial_withdrawal] * 10
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_insufficient_effective_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
# Make effective balance insufficient
state.validators[
validator_index
].effective_balance -= spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec,
state,
execution_layer_withdraw_request,
success=False,
)
@with_eip7251_and_later
@spec_state_test
def test_partial_withdrawal_incorrect_source_address(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
incorrect_address = b"\x33" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=incorrect_address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_partial_withdrawal_incorrect_withdrawal_credential_prefix(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
# Set incorrect prefix
state.validators[validator_index].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX
+ state.validators[validator_index].withdrawal_credentials[1:]
)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_partial_withdrawal_on_exit_initiated_validator(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
# Initiate exit earlier
spec.initiate_validator_exit(state, validator_index)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@with_eip7251_and_later
@spec_state_test
def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
spec, state
):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
state.balances[validator_index] += 2 * amount
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
execution_layer_withdraw_request = spec.ExecutionLayerWithdrawRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=amount,
)
assert spec.get_current_epoch(state) < (
state.validators[validator_index].activation_epoch
+ spec.config.SHARD_COMMITTEE_PERIOD
)
partial_withdrawal = spec.PendingPartialWithdrawal(index=0, amount=1, withdrawable_epoch=current_epoch)
state.pending_partial_withdrawals = [partial_withdrawal] * spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
yield from run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, success=False
)
@ -165,6 +748,7 @@ def test_partial_withdrawal_queue_full(spec, state):
# Run processing
#
def run_execution_layer_withdraw_request_processing(
spec, state, execution_layer_withdraw_request, valid=True, success=True
):
@ -176,38 +760,74 @@ def run_execution_layer_withdraw_request_processing(
If ``valid == False``, run expecting ``AssertionError``
If ``success == False``, it doesn't initiate exit successfully
"""
validator_index = get_validator_index_by_pubkey(state, execution_layer_withdraw_request.validator_pubkey)
validator_index = get_validator_index_by_pubkey(
state, execution_layer_withdraw_request.validator_pubkey
)
yield 'pre', state
yield 'execution_layer_withdraw_request', execution_layer_withdraw_request
yield "pre", state
yield "execution_layer_withdraw_request", execution_layer_withdraw_request
if not valid:
expect_assertion_error(
lambda: spec.process_execution_layer_withdraw_request(state, execution_layer_withdraw_request))
yield 'post', None
lambda: spec.process_execution_layer_withdraw_request(
state, execution_layer_withdraw_request
)
)
yield "post", None
return
pre_exit_epoch = state.validators[validator_index].exit_epoch
pre_pending_partial_withdrawals = state.pending_partial_withdrawals
pre_pending_partial_withdrawals = state.pending_partial_withdrawals.copy()
pre_balance = state.balances[validator_index]
pre_effective_balance = state.validators[validator_index].effective_balance
pre_state = state
expected_amount_to_withdraw = compute_amount_to_withdraw(
spec, state, validator_index, execution_layer_withdraw_request.amount
)
spec.process_execution_layer_withdraw_request(state, execution_layer_withdraw_request)
spec.process_execution_layer_withdraw_request(
state, execution_layer_withdraw_request
)
yield 'post', state
yield "post", state
if execution_layer_withdraw_request.amount == 0:
if success:
if not success:
# No-op
assert pre_state == state
else:
assert state.balances[validator_index] == pre_balance
assert (
state.validators[validator_index].effective_balance == pre_effective_balance
)
# Full exit request
if execution_layer_withdraw_request.amount == spec.FULL_EXIT_REQUEST_AMOUNT:
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[validator_index].exit_epoch == pre_exit_epoch
else:
if success:
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.balances[validator_index] == pre_balance
post_length = len(state.pending_partial_withdrawals)
assert post_length == len(pre_pending_partial_withdrawals) + 1
assert post_length < spec.PENDING_PARTIAL_WITHDRAWALS_LIMIT
assert state.pending_partial_withdrawals[post_length - 1].validator_index == validator_index
else:
assert spec.get_pending_balance_to_withdraw(state, validator_index) == 0
assert state.pending_partial_withdrawals == pre_pending_partial_withdrawals
# Partial withdrawal request
else:
assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
expected_withdrawable_epoch = (
state.earliest_exit_epoch
+ spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
expected_partial_withdrawal = spec.PendingPartialWithdrawal(
index=validator_index,
amount=expected_amount_to_withdraw,
withdrawable_epoch=expected_withdrawable_epoch,
)
assert (
state.pending_partial_withdrawals
== pre_pending_partial_withdrawals + [expected_partial_withdrawal]
)
def compute_amount_to_withdraw(spec, state, index, amount):
pending_balance_to_withdraw = spec.get_pending_balance_to_withdraw(state, index)
return min(
state.balances[index]
- spec.MIN_ACTIVATION_BALANCE
- pending_balance_to_withdraw,
amount,
)

View File

@ -0,0 +1,411 @@
from eth2spec.test.helpers.constants import MAINNET
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
with_presets,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import (
run_voluntary_exit_processing,
sign_voluntary_exit,
)
# ********************
# * EXIT QUEUE TESTS *
# ********************
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exit(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# This state has 64 validators each with 32 ETH
current_epoch = spec.get_current_epoch(state)
expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
validator_index = spec.get_active_validator_indices(
state, spec.get_current_epoch(state)
)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Check exit queue churn is set correctly
assert state.exit_balance_to_consume == churn_limit - spec.MIN_ACTIVATION_BALANCE
# Check exit epoch and withdrawable epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
assert state.validators[0].withdrawable_epoch == expected_withdrawable_epoch
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exits_up_to_churn(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
num_to_exit = churn_limit // single_validator_balance
# Exit all but 1 validators, all fit in the churn limit
for i in range(num_to_exit - 1):
validator_index = spec.get_active_validator_indices(
state, spec.get_current_epoch(state)
)[i]
spec.initiate_validator_exit(state, validator_index)
validator_index = spec.get_active_validator_indices(
state, spec.get_current_epoch(state)
)[num_to_exit]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(
epoch=spec.get_current_epoch(state), validator_index=validator_index
),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Last validator also fits in the churn limit
expected_exit_epoch = spec.compute_activation_exit_epoch(
spec.get_current_epoch(state)
)
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
# Check exit epoch and withdrawable epoch
assert state.validators[num_to_exit].exit_epoch == expected_exit_epoch
assert (
state.validators[num_to_exit].withdrawable_epoch == expected_withdrawable_epoch
)
# Check exit queue churn is set
assert (
state.exit_balance_to_consume
== churn_limit - single_validator_balance * num_to_exit
)
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exits_above_churn(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
expected_exit_epoch = spec.compute_activation_exit_epoch(
spec.get_current_epoch(state)
)
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
num_to_exit = churn_limit // single_validator_balance
# Exit all but 1 validators, all fit in the churn limit
for i in range(num_to_exit):
validator_index = spec.get_active_validator_indices(
state, spec.get_current_epoch(state)
)[i]
spec.initiate_validator_exit(state, validator_index)
# Exit one more validator, not fitting in the churn limit
validator_index = spec.get_active_validator_indices(
state, spec.get_current_epoch(state)
)[num_to_exit]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(
epoch=spec.get_current_epoch(state), validator_index=validator_index
),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Check exit epoch and withdrawable epoch. Last validator exits one epoch later
assert state.validators[num_to_exit].exit_epoch == expected_exit_epoch + 1
assert (
state.validators[num_to_exit].withdrawable_epoch
== expected_withdrawable_epoch + 1
)
# Check exit balance to consume is set correctly
remainder = (num_to_exit + 1) * single_validator_balance % churn_limit
assert state.exit_balance_to_consume == churn_limit - remainder
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch + 1
@with_eip7251_and_later
@spec_state_test
@with_presets(
[MAINNET],
"With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit",
)
def test_max_balance_exit(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set validator effective balance to 2048 ETH
to_exit = spec.MAX_EFFECTIVE_BALANCE_EIP7251
state.validators[validator_index].effective_balance = to_exit
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Check exit epoch and withdrawable epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(
spec.get_current_epoch(state)
)
expected_exit_epoch += to_exit // churn_limit
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
assert (
state.validators[validator_index].withdrawable_epoch
== expected_withdrawable_epoch
)
# Check exit_balance_to_consume
remainder = to_exit % churn_limit
assert state.exit_balance_to_consume == churn_limit - remainder
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets(
[MAINNET],
"With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit",
)
def test_exit_with_balance_equal_to_churn_limit(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set 0th validator effective balance to churn_limit
state.validators[validator_index].effective_balance = churn_limit
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Validator consumes churn limit fully in the current epoch
expected_exit_epoch = spec.compute_activation_exit_epoch(
spec.get_current_epoch(state)
)
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
assert (
state.validators[validator_index].withdrawable_epoch
== expected_withdrawable_epoch
)
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == 0
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets(
[MAINNET],
"With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit",
)
def test_exit_with_balance_multiple_of_churn_limit(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
# Set validator effective balance to a multiple of churn_limit
epochs_to_consume = 3
state.validators[0].effective_balance = epochs_to_consume * churn_limit
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Validator consumes churn limit fully in the next 3 epochs (current included)
expected_exit_epoch = (
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
+ epochs_to_consume
)
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
assert (
state.validators[validator_index].withdrawable_epoch
== expected_withdrawable_epoch
)
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == churn_limit
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets(
[MAINNET],
"With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit",
)
def test_exit_existing_churn_and_churn_limit_balance(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
# set exit epoch to the first available one and set exit balance to consume to full churn limit
earliest_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_exit_epoch = earliest_exit_epoch
state.exit_balance_to_consume = churn_limit
# consume some churn in exit epoch
existing_churn = spec.EFFECTIVE_BALANCE_INCREMENT
state.exit_balance_to_consume -= existing_churn
# Set validator effective balance to the churn limit
state.validators[validator_index].effective_balance = churn_limit
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
expected_exit_epoch = earliest_exit_epoch + 1
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
assert (
state.validators[validator_index].withdrawable_epoch
== expected_withdrawable_epoch
)
# Check balance consumed in exit epoch is the remainder 1 ETH
assert state.exit_balance_to_consume == churn_limit - existing_churn
# check earliest exit epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets(
[MAINNET],
"With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit",
)
def test_exit_existing_churn_and_balance_multiple_of_churn_limit(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
# set exit epoch to the first available one and set exit balance to consume to full churn limit
earliest_exit_epoch = spec.compute_activation_exit_epoch(current_epoch)
state.earliest_exit_epoch = earliest_exit_epoch
state.exit_balance_to_consume = churn_limit
# consume some churn in exit epoch
existing_churn = spec.EFFECTIVE_BALANCE_INCREMENT
state.exit_balance_to_consume -= existing_churn
# Set validator effective balance to a multiple of churn_limit
epochs_to_consume = 3
state.validators[validator_index].effective_balance = (
epochs_to_consume * churn_limit
)
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec,
state,
spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index),
privkey,
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
# Validator fully consumes epochs_to_consume and gets into the next one
expected_exit_epoch = earliest_exit_epoch + epochs_to_consume
expected_withdrawable_epoch = (
expected_exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
assert (
state.validators[validator_index].withdrawable_epoch
== expected_withdrawable_epoch
)
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == churn_limit - existing_churn
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_has_pending_withdrawal(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
state.pending_partial_withdrawals.append(
spec.PendingPartialWithdrawal(
index=validator_index,
amount=1,
withdrawable_epoch=spec.compute_activation_exit_epoch(current_epoch),
)
)
yield from run_voluntary_exit_processing(
spec, state, signed_voluntary_exit, valid=False
)

View File

@ -1,441 +0,0 @@
from eth2spec.test.helpers.constants import (MINIMAL, MAINNET)
from eth2spec.test.context import (
spec_state_test,
with_eip7251_and_later,
with_presets,
always_bls,
spec_test, single_phase,
with_custom_state,
scaled_churn_balances_min_churn_limit,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import (
run_voluntary_exit_processing,
sign_voluntary_exit,
)
# ********************
# * EXIT QUEUE TESTS *
# ********************
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exit(spec, state):
# This state has 64 validators each with 32 ETH
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
spec.initiate_validator_exit(state, 0)
yield "post", state
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - spec.MIN_ACTIVATION_BALANCE
# Check exit epoch
assert state.validators[0].exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exits_up_to_churn(spec, state):
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
for i in range(churn_limit // spec.MIN_ACTIVATION_BALANCE):
validator_index = i
spec.initiate_validator_exit(state, validator_index)
yield f"post{i}", state
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance * (i + 1)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
yield "post", state
@with_eip7251_and_later
@spec_state_test
def test_min_balance_exits_above_churn(spec, state):
# This state has 64 validators each with 32 ETH
single_validator_balance = spec.MIN_ACTIVATION_BALANCE
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set the balance to consume equal to churn limit
state.exit_balance_to_consume = churn_limit
yield "pre", state
# Exit validators, all which fit in the churn limit
for i in range(churn_limit // spec.MIN_ACTIVATION_BALANCE):
validator_index = i
spec.initiate_validator_exit(state, validator_index)
# Check exit queue churn is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance * (i + 1)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Exit balance has been fully consumed
assert state.exit_balance_to_consume == 0
# Exit an additional validator, doesn't fit in the churn limit, so exit
# epoch is incremented
validator_index = churn_limit // spec.MIN_ACTIVATION_BALANCE
spec.initiate_validator_exit(state, validator_index)
yield "post", state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch + 1
# Check exit balance to consume is set
assert state.exit_balance_to_consume == churn_limit - single_validator_balance
# @with_eip7251_and_later
# @spec_state_test
# def test_exit_balance_to_consume_large_validator(spec, state):
# # Set 0th validator effective balance to 2048 ETH
# state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE_EIP7251
# churn_limit = spec.get_validator_churn_limit(state)
# expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
# expected_exit_epoch += spec.MAX_EFFECTIVE_BALANCE_EIP7251 // churn_limit
# validator_index = 0
# spec.initiate_validator_exit(state, validator_index)
# # Check exit epoch
# assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# # Check exit_balance_to_consume
# assert state.exit_balance_to_consume == churn_limit - (spec.MAX_EFFECTIVE_BALANCE_EIP7251 % churn_limit)
# # Check earliest_exit_epoch
# assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_max_balance_exit(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
assert churn_limit == spec.MIN_ACTIVATION_BALANCE * spec.config.MIN_PER_EPOCH_CHURN_LIMIT
# Set 0th validator effective balance to 2048 ETH
state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE_EIP7251
yield 'pre', state
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
# Validator consumes exit churn for 16 epochs, exits at the 17th one
expected_exit_epoch += (spec.MAX_EFFECTIVE_BALANCE_EIP7251 // churn_limit)
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == churn_limit
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == expected_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_exit_with_balance_equal_to_churn_limit(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
# Set 0th validator effective balance to churn_limit
state.validators[0].effective_balance = churn_limit
yield 'pre', state
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Validator consumes churn limit fully in the current epoch
assert (state.validators[validator_index].exit_epoch ==
spec.compute_activation_exit_epoch(spec.get_current_epoch(state)))
# Check exit_balance_to_consume
assert state.exit_balance_to_consume == 0
# Check earliest_exit_epoch
assert state.earliest_exit_epoch == state.validators[validator_index].exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_exit_churn_limit_balance_existing_churn_(spec, state):
cl = spec.get_activation_exit_churn_limit(state)
# set exit epoch to the first available one and set exit balance to consume to full churn limit
state.earliest_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
state.exit_balance_to_consume = cl
# consume some churn in exit epoch
state.exit_balance_to_consume -= 1000000000
# Set 0th validator effective balance to the churn limit
state.validators[0].effective_balance = cl
yield 'pre', state
# The existing 1 ETH churn will push an extra epoch
expected_exit_epoch = state.earliest_exit_epoch + 1
yield 'post', state
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check balance consumed in exit epoch is the remainder 1 ETH
assert state.exit_balance_to_consume == cl - 1000000000
# check earliest exit epoch
assert expected_exit_epoch == state.earliest_exit_epoch
@with_eip7251_and_later
@spec_state_test
@with_presets([MAINNET], "With CHURN_LIMIT_QUOTIENT=32, can't change validator balance without changing churn_limit")
def test_multi_epoch_exit_existing_churn(spec, state):
cl = spec.get_activation_exit_churn_limit(state)
# set exit epoch to the first available one and set exit balance to consume to full churn limit
state.earliest_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
state.exit_balance_to_consume = cl
# consume some churn in exit epoch
state.exit_balance_to_consume -= 1000000000
# Set 0th validator effective balance to 2x the churn limit
state.validators[0].effective_balance = 2 * cl
yield 'pre', state
# Two extra epochs will be necessary
expected_exit_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state)) + 2
validator_index = 0
spec.initiate_validator_exit(state, validator_index)
yield 'post', state
# Check exit epoch
assert state.validators[validator_index].exit_epoch == expected_exit_epoch
# Check balance consumed in exit epoch is the remainder 1 ETH
assert state.exit_balance_to_consume == cl - 1000000000
# check earliest exit epoch
assert expected_exit_epoch == state.earliest_exit_epoch
# Repurposed from phase0 voluntary exit tests, should disable the phase0 ones
def run_test_success_exit_queue(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
churn_limit = spec.get_activation_exit_churn_limit(state)
# exit `MAX_EXITS_PER_EPOCH`
max_exits = churn_limit // spec.MIN_ACTIVATION_BALANCE
initial_indices = spec.get_active_validator_indices(state, current_epoch)[:max_exits]
# Prepare a bunch of exits, based on the current state
exit_queue = []
for index in initial_indices:
privkey = pubkey_to_privkey[state.validators[index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=index), privkey)
exit_queue.append(signed_voluntary_exit)
# Now run all the exits
for voluntary_exit in exit_queue:
# the function yields data, but we are just interested in running it here, ignore yields.
for _ in run_voluntary_exit_processing(spec, state, voluntary_exit):
continue
# exit an additional validator
validator_index = spec.get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
# This is the interesting part of the test: on a pre-state with a full exit queue,
# 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)
for index in initial_indices:
assert (
state.validators[validator_index].exit_epoch ==
state.validators[index].exit_epoch + 1
)
assert state.earliest_exit_epoch == state.validators[validator_index].exit_epoch
consumed_churn = spec.MIN_ACTIVATION_BALANCE * (max_exits + 1)
assert state.exit_balance_to_consume == churn_limit - (consumed_churn % churn_limit)
@with_eip7251_and_later
@spec_state_test
def test_success_exit_queue__min_churn(spec, state):
yield from run_test_success_exit_queue(spec, state)
@with_eip7251_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_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_success_exit_queue__scaled_churn(spec, state):
churn_limit = spec.get_activation_exit_churn_limit(state)
assert churn_limit > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
yield from run_test_success_exit_queue(spec, state)
# After here no modifications were made, can just leave them in phase0 as is
@with_eip7251_and_later
@spec_state_test
def test_basic(spec, state):
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch)
@with_eip7251_and_later
@spec_state_test
@always_bls
def test_invalid_incorrect_signature(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, 12345)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_default_exit_epoch_subsequent_exit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
# Exit one validator prior to this new one
exited_index = spec.get_active_validator_indices(state, current_epoch)[-1]
state.validators[exited_index].exit_epoch = current_epoch - 1
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
assert state.validators[validator_index].exit_epoch == spec.compute_activation_exit_epoch(current_epoch)
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_exit_in_future(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch + 1,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_incorrect_validator_index(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=len(state.validators),
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_not_active(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_already_exited(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow validator able to exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
# but validator already has exited
state.validators[validator_index].exit_epoch = current_epoch + 2
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)
@with_eip7251_and_later
@spec_state_test
def test_invalid_validator_not_active_long_enough(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
assert (
current_epoch - state.validators[validator_index].activation_epoch <
spec.config.SHARD_COMMITTEE_PERIOD
)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=False)

View File

@ -0,0 +1,12 @@
from eth2spec.test.context import spec_state_test, with_eip7251_and_later
from eth2spec.test.phase0.epoch_processing.test_process_effective_balance_updates import (
run_test_effective_balance_hysteresis,
)
@with_eip7251_and_later
@spec_state_test
def test_effective_balance_hysteresis_with_compounding_credentials(spec, state):
run_test_effective_balance_hysteresis(
spec, state, with_compounding_credentials=True
)

View File

@ -10,9 +10,13 @@ from eth2spec.test.context import (
def test_pending_deposit_min_activation_balance(spec, state):
index = 0
amount = spec.MIN_ACTIVATION_BALANCE
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
assert state.deposit_balance_to_consume == 0
@ -24,9 +28,13 @@ def test_pending_deposit_min_activation_balance(spec, state):
def test_pending_deposit_balance_equal_churn(spec, state):
index = 0
amount = spec.get_activation_exit_churn_limit(state)
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
assert state.balances[index] == pre_balance + amount
assert state.deposit_balance_to_consume == 0
assert state.pending_balance_deposits == []
@ -37,26 +45,38 @@ def test_pending_deposit_balance_equal_churn(spec, state):
def test_pending_deposit_balance_above_churn(spec, state):
index = 0
amount = spec.get_activation_exit_churn_limit(state) + 1
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
# deposit was above churn, balance hasn't changed
assert state.balances[index] == pre_balance
# deposit balance to consume is the full churn limit
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state)
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(
state
)
# deposit is still in the queue
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=index, amount=amount)]
assert state.pending_balance_deposits == [
spec.PendingBalanceDeposit(index=index, amount=amount)
]
@with_eip7251_and_later
@spec_state_test
def test_pending_deposit_preexisting_churn(spec, state):
index = 0
amount = 10 ** 9 + 1
amount = 10**9 + 1
state.deposit_balance_to_consume = 2 * amount
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=index, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=index, amount=amount)
)
pre_balance = state.balances[index]
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
# balance was deposited correctly
assert state.balances[index] == pre_balance + amount
# No leftover deposit balance to consume when there are no deposits left to process
@ -69,10 +89,16 @@ def test_pending_deposit_preexisting_churn(spec, state):
@spec_state_test
def test_multiple_pending_deposits_below_churn(spec, state):
amount = 10**9
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=0, amount=amount))
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=1, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=0, amount=amount)
)
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=1, amount=amount)
)
pre_balances = state.balances
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
for i in [0, 1]:
assert state.balances[i] == pre_balances[i] + amount
# No leftover deposit balance to consume when there are no deposits left to process
@ -86,14 +112,23 @@ def test_multiple_pending_deposits_above_churn(spec, state):
# set third deposit to be over the churn
amount = (spec.get_activation_exit_churn_limit(state) // 3) + 1
for i in [0, 1, 2]:
state.pending_balance_deposits.append(spec.PendingBalanceDeposit(index=i, amount=amount))
state.pending_balance_deposits.append(
spec.PendingBalanceDeposit(index=i, amount=amount)
)
pre_balances = state.balances
yield from run_epoch_processing_with(spec, state, 'process_pending_balance_deposits')
yield from run_epoch_processing_with(
spec, state, "process_pending_balance_deposits"
)
# First two deposits are processed, third is not because above churn
for i in [0, 1]:
assert state.balances[i] == pre_balances[i] + amount
assert state.balances[2] == pre_balances[2]
# Only first two subtract from the deposit balance to consume
assert state.deposit_balance_to_consume == spec.get_activation_exit_churn_limit(state) - 2 * amount
assert (
state.deposit_balance_to_consume
== spec.get_activation_exit_churn_limit(state) - 2 * amount
)
# third deposit is still in the queue
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(index=2, amount=amount)]
assert state.pending_balance_deposits == [
spec.PendingBalanceDeposit(index=2, amount=amount)
]

View File

@ -16,13 +16,66 @@ def test_basic_pending_consolidation(spec, state):
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# append pending consolidation
state.pending_consolidations.append(spec.PendingConsolidation(source_index=source_index, target_index=target_index))
state.pending_consolidations.append(
spec.PendingConsolidation(source_index=source_index, target_index=target_index)
)
# Set withdrawable epoch to current epoch to allow processing
state.validators[source_index].withdrawable_epoch = spec.get_current_epoch(state)
state.validators[source_index].withdrawable_epoch = current_epoch
# Set the target withdrawal credential to eth1
eth1_withdrawal_credential = (
spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b"\x00" * 11 + b"\x11" * 20
)
state.validators[target_index].withdrawal_credentials = eth1_withdrawal_credential
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")
# Pending consolidation was successfully processed
assert (
state.validators[target_index].withdrawal_credentials[:1]
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
)
assert state.balances[target_index] == 2 * spec.MIN_ACTIVATION_BALANCE
assert state.balances[source_index] == 0
assert state.pending_consolidations == []
@with_eip7251_and_later
@spec_state_test
def test_consolidation_not_yet_withdrawable_validator(spec, state):
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
target_index = spec.get_active_validator_indices(state, current_epoch)[1]
# append pending consolidation
state.pending_consolidations.append(
spec.PendingConsolidation(source_index=source_index, target_index=target_index)
)
# Set the target to eth1 withdrawal credentials
eth1_withdrawal_credential = (
spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b"\x00" * 11 + b"\x11" * 20
)
state.validators[target_index].withdrawal_credentials = eth1_withdrawal_credential
# Initiate exit of source validator
spec.initiate_validator_exit(state, source_index)
pre_pending_consolidations = state.pending_consolidations.copy()
pre_balances = state.balances.copy()
pre_target_withdrawal_credential = state.validators[
target_index
].withdrawal_credentials[:1]
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")
# Pending consolidation is not processed
# Balances are unchanged
assert state.balances[source_index] == pre_balances[0]
assert state.balances[target_index] == pre_balances[1]
# Target withdrawal credential is unchanged
assert (
state.validators[target_index].withdrawal_credentials[:1]
== pre_target_withdrawal_credential
)
# Pending consolidation is still in the queue
assert state.pending_consolidations == pre_pending_consolidations
@with_eip7251_and_later
@ -35,12 +88,22 @@ def test_skip_consolidation_when_source_slashed(spec, state):
target1_index = spec.get_active_validator_indices(state, current_epoch)[3]
# append pending consolidation
state.pending_consolidations.append(
spec.PendingConsolidation(source_index=source0_index, target_index=target0_index)
spec.PendingConsolidation(
source_index=source0_index, target_index=target0_index
)
)
state.pending_consolidations.append(
spec.PendingConsolidation(source_index=source1_index, target_index=target1_index)
spec.PendingConsolidation(
source_index=source1_index, target_index=target1_index
)
)
eth1_withdrawal_credential = (
spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b"\x00" * 11 + b"\x11" * 20
)
state.validators[target0_index].withdrawal_credentials = eth1_withdrawal_credential
state.validators[target1_index].withdrawal_credentials = eth1_withdrawal_credential
# Set withdrawable epoch of sources to current epoch to allow processing
state.validators[source0_index].withdrawable_epoch = spec.get_current_epoch(state)
state.validators[source1_index].withdrawable_epoch = spec.get_current_epoch(state)
@ -52,6 +115,73 @@ def test_skip_consolidation_when_source_slashed(spec, state):
# first pending consolidation should not be processed
assert state.balances[target0_index] == spec.MIN_ACTIVATION_BALANCE
assert state.balances[source0_index] == spec.MIN_ACTIVATION_BALANCE
assert (
state.validators[target0_index].withdrawal_credentials[:1]
== spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
)
# second pending consolidation should be processed: first one is skipped and doesn't block the queue
assert state.balances[target1_index] == 2 * spec.MIN_ACTIVATION_BALANCE
assert state.balances[source1_index] == 0
assert (
state.validators[target1_index].withdrawal_credentials[:1]
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
)
@with_eip7251_and_later
@spec_state_test
def test_all_consolidation_cases_together(spec, state):
current_epoch = spec.get_current_epoch(state)
source_index = [
spec.get_active_validator_indices(state, current_epoch)[i] for i in range(4)
]
target_index = [
spec.get_active_validator_indices(state, current_epoch)[4 + i] for i in range(4)
]
state.pending_consolidations = [
spec.PendingConsolidation(
source_index=source_index[i], target_index=target_index[i]
)
for i in range(4)
]
# Set withdrawable epoch to current epoch for first and last source validators
for i in [0, 2]:
state.validators[source_index[i]].withdrawable_epoch = current_epoch
# Set second source validator as slashed
state.validators[source_index[1]].slashed = True
# Set targets withdrawal credentials to eth1
eth1_withdrawal_credential = (
spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b"\x00" * 11 + b"\x11" * 20
)
for i in range(4):
state.validators[target_index[i]].withdrawal_credentials = (
eth1_withdrawal_credential
)
# Initiate exit of third source validator
spec.initiate_validator_exit(state, 2)
pre_balances = state.balances.copy()
pre_target_withdrawal_prefixes = [
state.validators[target_index[i]].withdrawal_credentials[:1]
for i in [0, 1, 2, 3]
]
pre_pending_consolidations = state.pending_consolidations.copy()
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")
# First consolidation is successfully processed
assert (
state.validators[target_index[0]].withdrawal_credentials[:1]
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
)
assert state.balances[target_index[0]] == 2 * spec.MIN_ACTIVATION_BALANCE
assert state.balances[source_index[0]] == 0
# All other consolidations are not processed
for i in [1, 2, 3]:
assert (
state.validators[target_index[i]].withdrawal_credentials[:1]
== pre_target_withdrawal_prefixes[i]
)
assert state.balances[source_index[i]] == pre_balances[source_index[i]]
assert state.balances[target_index[i]] == pre_balances[target_index[i]]
# First consolidation is processed, second is skipped, last two are left in the queue
state.pending_consolidations = pre_pending_consolidations[2:]

View File

@ -1,7 +1,7 @@
from random import Random
from eth2spec.test.context import expect_assertion_error
from eth2spec.test.helpers.forks import is_post_altair
from eth2spec.test.helpers.forks import is_post_altair, is_post_eip7251
from eth2spec.test.helpers.keys import pubkeys, privkeys
from eth2spec.test.helpers.state import get_balance
from eth2spec.utils import bls
@ -234,6 +234,7 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
"""
pre_validator_count = len(state.validators)
pre_balance = 0
pre_effective_balance = 0
is_top_up = False
# is a top-up
if validator_index < pre_validator_count:
@ -241,6 +242,9 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
pre_balance = get_balance(state, validator_index)
pre_effective_balance = state.validators[validator_index].effective_balance
if is_post_eip7251(spec):
pre_pending_deposits = len(state.pending_balance_deposits)
yield 'pre', state
yield 'deposit', deposit
@ -260,19 +264,30 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef
assert get_balance(state, validator_index) == pre_balance
else:
if is_top_up:
# Top-ups do not change effective balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
# Top-ups don't add validators
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
# new validator is added
assert len(state.validators) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit.data.amount)
effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT
assert state.validators[validator_index].effective_balance == effective_balance
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
if not is_post_eip7251(spec):
if is_top_up:
# Top-ups do not change effective balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
else:
effective_balance = min(spec.MAX_EFFECTIVE_BALANCE, deposit.data.amount)
effective_balance -= effective_balance % spec.EFFECTIVE_BALANCE_INCREMENT
assert state.validators[validator_index].effective_balance == effective_balance
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
else:
# no balance or effective balance changes on deposit processing post eip7251
assert get_balance(state, validator_index) == pre_balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
# new correct balance deposit queued up
assert len(state.pending_balance_deposits) == pre_pending_deposits + 1
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit.data.amount
assert state.pending_balance_deposits[pre_pending_deposits].index == validator_index
assert state.eth1_deposit_index == state.eth1_data.deposit_count
@ -390,88 +405,3 @@ def run_deposit_receipt_processing_with_specific_fork_version(
valid=valid,
effective=effective
)
# ********************
# * EIP7251 *
# ********************
def run_deposit_processing_eip7251(spec, state, deposit, validator_index, valid=True, effective=True):
"""
Run ``process_deposit``, yielding:
- pre-state ('pre')
- deposit ('deposit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
pre_validator_count = len(state.validators)
pre_pending_deposits = len(state.pending_balance_deposits)
pre_balance = 0
pre_effective_balance = 0
is_top_up = False
# is a top-up
if validator_index < pre_validator_count:
is_top_up = True
pre_balance = get_balance(state, validator_index)
pre_effective_balance = state.validators[validator_index].effective_balance
yield 'pre', state
yield 'deposit', deposit
if not valid:
expect_assertion_error(lambda: spec.process_deposit(state, deposit))
yield 'post', None
return
spec.process_deposit(state, deposit)
yield 'post', state
if not effective or not bls.KeyValidate(deposit.data.pubkey):
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# no balance changes on deposit processing
assert get_balance(state, validator_index) == pre_balance
assert state.validators[validator_index].effective_balance == pre_effective_balance
if is_top_up:
assert len(state.validators) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
assert len(state.validators) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
# new correct balance deposit has been appended
assert len(state.pending_balance_deposits) == pre_pending_deposits + 1
assert state.pending_balance_deposits[pre_pending_deposits].amount == deposit.data.amount
assert state.pending_balance_deposits[pre_pending_deposits].index == validator_index
assert state.eth1_deposit_index == state.eth1_data.deposit_count
def run_deposit_processing_eip7251_with_specific_fork_version(
spec,
state,
fork_version,
valid=True,
effective=True):
validator_index = len(state.validators)
amount = spec.MAX_EFFECTIVE_BALANCE
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:]
deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version)
deposit_data = spec.DepositData(
pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount,
signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain))
)
deposit, root, _ = deposit_from_context(spec, [deposit_data], 0)
state.eth1_deposit_index = 0
state.eth1_data.deposit_root = root
state.eth1_data.deposit_count = 1
yield from run_deposit_processing_eip7251(spec, state, deposit, validator_index, valid=valid, effective=effective)

View File

@ -4,6 +4,7 @@ from rlp import encode
from rlp.sedes import big_endian_int, Binary, List
from eth2spec.debug.random_value import get_random_bytes_list
from eth2spec.test.helpers.withdrawals import get_expected_withdrawals
from eth2spec.test.helpers.forks import (
is_post_capella,
is_post_deneb,
@ -223,7 +224,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
transactions=empty_txs,
)
if is_post_capella(spec):
payload.withdrawals = spec.get_expected_withdrawals(state)
payload.withdrawals = get_expected_withdrawals(spec, state)
if is_post_deneb(spec):
payload.blob_gas_used = 0
payload.excess_blob_gas = 0

View File

@ -152,7 +152,10 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
state.deposit_balance_to_consume = 0
state.exit_balance_to_consume = 0
state.earliest_exit_epoch = spec.GENESIS_EPOCH
state.consolidation_balance_to_consume = 0
state.earliest_consolidation_epoch = 0
state.pending_balance_deposits = []
state.pending_partial_withdrawals = []
state.pending_consolidations = []
return state

View File

@ -1,5 +1,5 @@
from eth2spec.test.helpers.block_header import sign_block_header
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix, is_post_eip7251
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.state import get_balance
from eth2spec.test.helpers.sync_committee import (
@ -9,7 +9,9 @@ from eth2spec.test.helpers.sync_committee import (
def get_min_slashing_penalty_quotient(spec):
if is_post_bellatrix(spec):
if is_post_eip7251(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_EIP7251
elif is_post_bellatrix(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_BELLATRIX
elif is_post_altair(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR
@ -17,6 +19,13 @@ def get_min_slashing_penalty_quotient(spec):
return spec.MIN_SLASHING_PENALTY_QUOTIENT
def get_whistleblower_reward_quotient(spec):
if is_post_eip7251(spec):
return spec.WHISTLEBLOWER_REWARD_QUOTIENT_EIP7251
else:
return spec.WHISTLEBLOWER_REWARD_QUOTIENT
def check_proposer_slashing_effect(spec, pre_state, state, slashed_index, block=None):
slashed_validator = state.validators[slashed_index]
assert slashed_validator.slashed
@ -25,7 +34,7 @@ def check_proposer_slashing_effect(spec, pre_state, state, slashed_index, block=
proposer_index = spec.get_beacon_proposer_index(state)
slash_penalty = state.validators[slashed_index].effective_balance // get_min_slashing_penalty_quotient(spec)
whistleblower_reward = state.validators[slashed_index].effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
whistleblower_reward = state.validators[slashed_index].effective_balance // get_whistleblower_reward_quotient(spec)
# Altair introduces sync committee (SC) reward and penalty
sc_reward_for_slashed = sc_penalty_for_slashed = sc_reward_for_proposer = sc_penalty_for_proposer = 0

View File

@ -1,4 +1,13 @@
import random
from eth2spec.test.helpers.forks import is_post_eip7251
def get_expected_withdrawals(spec, state):
if is_post_eip7251(spec):
withdrawals, _ = spec.get_expected_withdrawals(state)
return withdrawals
else:
return spec.get_expected_withdrawals(state)
def set_validator_fully_withdrawable(spec, state, index, withdrawable_epoch=None):

View File

@ -10,7 +10,10 @@ from eth2spec.test.helpers.attester_slashings import (
get_valid_attester_slashing, get_valid_attester_slashing_by_indices,
get_indexed_attestation_participants, get_attestation_2_data, get_attestation_1_data,
)
from eth2spec.test.helpers.proposer_slashings import get_min_slashing_penalty_quotient
from eth2spec.test.helpers.proposer_slashings import (
get_min_slashing_penalty_quotient,
get_whistleblower_reward_quotient,
)
from eth2spec.test.helpers.state import (
get_balance,
next_epoch_via_block,
@ -49,7 +52,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
}
total_proposer_rewards = sum(
effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
effective_balance // get_whistleblower_reward_quotient(spec)
for effective_balance in pre_slashing_effectives.values()
)
@ -71,7 +74,9 @@ 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_slashing_balances[slashed_index]
if slashed_index != proposer_index:
# NOTE: check proposer balances below
assert get_balance(state, slashed_index) < pre_slashing_balances[slashed_index]
if proposer_index not in slashed_indices:
# gained whistleblower reward

View File

@ -7,6 +7,7 @@ from eth2spec.test.helpers.deposits import (
sign_deposit_data,
)
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.forks import is_post_eip7251
@with_all_phases
@ -123,8 +124,9 @@ def test_top_up__max_effective_balance(spec, state):
yield from run_deposit_processing(spec, state, deposit, validator_index)
assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
if not is_post_eip7251(spec):
assert state.balances[validator_index] == spec.MAX_EFFECTIVE_BALANCE + amount
assert state.validators[validator_index].effective_balance == spec.MAX_EFFECTIVE_BALANCE
@with_all_phases
@ -141,9 +143,10 @@ def test_top_up__less_effective_balance(spec, state):
yield from run_deposit_processing(spec, state, deposit, validator_index)
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
if not is_post_eip7251(spec):
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_all_phases
@ -160,9 +163,10 @@ def test_top_up__zero_balance(spec, state):
yield from run_deposit_processing(spec, state, deposit, validator_index)
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
if not is_post_eip7251(spec):
assert state.balances[validator_index] == initial_balance + amount
# unchanged effective balance
assert state.validators[validator_index].effective_balance == initial_effective_balance
@with_all_phases

View File

@ -1,15 +1,24 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to
from eth2spec.test.helpers.withdrawals import (
set_compounding_withdrawal_credential,
)
from eth2spec.test.helpers.forks import is_post_eip7251
@with_all_phases
@spec_state_test
def test_effective_balance_hysteresis(spec, state):
run_test_effective_balance_hysteresis(spec, state)
def run_test_effective_balance_hysteresis(spec, state, with_compounding_credentials=False):
assert is_post_eip7251(spec) or not with_compounding_credentials
# Prepare state up to the final-updates.
# Then overwrite the balances, we only want to focus to be on the hysteresis based changes.
run_epoch_processing_to(spec, state, 'process_effective_balance_updates')
# Set some edge cases for balances
max = spec.MAX_EFFECTIVE_BALANCE
max = spec.MAX_EFFECTIVE_BALANCE_EIP_7251 if with_compounding_credentials else spec.MIN_ACTIVATION_BALANCE
min = spec.config.EJECTION_BALANCE
inc = spec.EFFECTIVE_BALANCE_INCREMENT
div = spec.HYSTERESIS_QUOTIENT
@ -35,6 +44,8 @@ def test_effective_balance_hysteresis(spec, state):
current_epoch = spec.get_current_epoch(state)
for i, (pre_eff, bal, _, _) in enumerate(cases):
assert spec.is_active_validator(state.validators[i], current_epoch)
if with_compounding_credentials:
set_compounding_withdrawal_credential(spec, state, i)
state.validators[i].effective_balance = pre_eff
state.balances[i] = bal

View File

@ -1,5 +1,6 @@
from eth2spec.test.helpers.deposits import mock_deposit
from eth2spec.test.helpers.state import next_epoch, next_slots
from eth2spec.test.helpers.forks import is_post_eip7251
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
spec_test, spec_state_test,
@ -105,17 +106,23 @@ def test_activation_queue_sorting(spec, state):
yield from run_process_registry_updates(spec, state)
# the first got in as second
assert state.validators[0].activation_epoch != spec.FAR_FUTURE_EPOCH
# the prioritized got in as first
assert state.validators[mock_activations - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
# the second last is at the end of the queue, and did not make the churn,
# hence is not assigned an activation_epoch yet.
assert state.validators[mock_activations - 2].activation_epoch == spec.FAR_FUTURE_EPOCH
# the one at churn_limit did not make it, it was out-prioritized
assert state.validators[churn_limit].activation_epoch == spec.FAR_FUTURE_EPOCH
# but the one in front of the above did
assert state.validators[churn_limit - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
if is_post_eip7251(spec):
# NOTE: EIP-7521 changed how activations are gated
# given the prefix setup here, all validators should be activated
activation_epochs = [state.validators[i].activation_epoch for i in range(mock_activations)]
assert all([epoch != spec.FAR_FUTURE_EPOCH for epoch in activation_epochs])
else:
# the first got in as second
assert state.validators[0].activation_epoch != spec.FAR_FUTURE_EPOCH
# the prioritized got in as first
assert state.validators[mock_activations - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
# the second last is at the end of the queue, and did not make the churn,
# hence is not assigned an activation_epoch yet.
assert state.validators[mock_activations - 2].activation_epoch == spec.FAR_FUTURE_EPOCH
# the one at churn_limit did not make it, it was out-prioritized
assert state.validators[churn_limit].activation_epoch == spec.FAR_FUTURE_EPOCH
# but the one in front of the above did
assert state.validators[churn_limit - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
def run_test_activation_queue_efficiency(spec, state):
@ -141,7 +148,9 @@ def run_test_activation_queue_efficiency(spec, state):
# Half should churn in first run of registry update
for i in range(mock_activations):
if i < churn_limit_0:
# NOTE: EIP-7251 changes how activations are gated
# given the prefix setup here, all validators are eligible for activation
if i < churn_limit_0 or is_post_eip7251(spec):
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH
@ -205,16 +214,30 @@ def run_test_ejection_past_churn_limit(spec, state):
yield from run_process_registry_updates(spec, state)
if is_post_eip7251(spec):
per_epoch_churn = spec.get_activation_exit_churn_limit(state)
def map_index_to_exit_epoch(i):
balance_so_far = i * spec.config.EJECTION_BALANCE
offset_epoch = balance_so_far // per_epoch_churn
if spec.config.EJECTION_BALANCE > per_epoch_churn - (balance_so_far % per_epoch_churn):
offset_epoch += 1
return expected_ejection_epoch + offset_epoch
else:
def map_index_to_exit_epoch(i):
# first third ejected in normal speed
if i < mock_ejections // 3:
return expected_ejection_epoch
# second third gets delayed by 1 epoch
elif mock_ejections // 3 <= i < mock_ejections * 2 // 3:
return expected_ejection_epoch + 1
# final third gets delayed by 2 epochs
else:
return expected_ejection_epoch + 2
for i in range(mock_ejections):
# first third ejected in normal speed
if i < mock_ejections // 3:
assert state.validators[i].exit_epoch == expected_ejection_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
# final third gets delayed by 2 epochs
else:
assert state.validators[i].exit_epoch == expected_ejection_epoch + 2
target_exit_epoch = map_index_to_exit_epoch(i)
assert state.validators[i].exit_epoch == target_exit_epoch
@with_all_phases
@ -286,7 +309,10 @@ def run_test_activation_queue_activation_and_ejection(spec, state, num_per_statu
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
# NOTE: activations are gated differently after EIP-7251
# all eligible validators were activated, regardless of churn limit
if not is_post_eip7251(spec):
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):
@ -364,9 +390,13 @@ def test_invalid_large_withdrawable_epoch(spec, state):
assert spec.is_active_validator(state.validators[0], spec.get_current_epoch(state))
assert spec.is_active_validator(state.validators[1], spec.get_current_epoch(state))
state.validators[0].exit_epoch = spec.FAR_FUTURE_EPOCH - 1
exit_epoch = spec.FAR_FUTURE_EPOCH - 1
state.validators[0].exit_epoch = exit_epoch
state.validators[1].effective_balance = spec.config.EJECTION_BALANCE
if is_post_eip7251(spec):
state.earliest_exit_epoch = exit_epoch
try:
yield from run_process_registry_updates(spec, state)
except ValueError:

View File

@ -34,6 +34,7 @@ from eth2spec.test.helpers.constants import PHASE0, MINIMAL
from eth2spec.test.helpers.forks import (
is_post_altair,
is_post_bellatrix,
is_post_eip7251,
is_post_capella,
)
from eth2spec.test.context import (
@ -743,9 +744,14 @@ def test_deposit_in_block(spec, state):
yield 'blocks', [signed_block]
yield 'post', state
if is_post_eip7251(spec):
balance = state.pending_balance_deposits[0].amount
else:
balance = get_balance(state, validator_index)
assert len(state.validators) == initial_registry_len + 1
assert len(state.balances) == initial_balances_len + 1
assert get_balance(state, validator_index) == spec.MAX_EFFECTIVE_BALANCE
assert balance == spec.MAX_EFFECTIVE_BALANCE
assert state.validators[validator_index].pubkey == pubkeys[validator_index]
@ -808,7 +814,11 @@ def test_deposit_top_up(spec, state):
committee_bits,
)
assert get_balance(state, validator_index) == (
balance = get_balance(state, validator_index)
if is_post_eip7251(spec):
balance += state.pending_balance_deposits[0].amount
assert balance == (
validator_pre_balance + amount + sync_committee_reward - sync_committee_penalty
)