Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

electra: Epoch processing tests #3984

Merged
merged 6 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions specs/electra/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,23 +778,25 @@ def process_epoch(state: BeaconState) -> None:

#### Modified `process_registry_updates`

*Note*: The function `process_registry_updates` is modified to use the updated definition of `initiate_validator_exit`
*Note*: The function `process_registry_updates` is modified to
use the updated definitions of `initiate_validator_exit` and `is_eligible_for_activation_queue`
and changes how the activation epochs are computed for eligible validators.

```python
def process_registry_updates(state: BeaconState) -> None:
# Process activation eligibility and ejections
for index, validator in enumerate(state.validators):
if is_eligible_for_activation_queue(validator):
if is_eligible_for_activation_queue(validator): # [Modified in Electra:EIP7251]
validator.activation_eligibility_epoch = get_current_epoch(state) + 1

if (
is_active_validator(validator, get_current_epoch(state))
and validator.effective_balance <= EJECTION_BALANCE
):
initiate_validator_exit(state, ValidatorIndex(index))
initiate_validator_exit(state, ValidatorIndex(index)) # [Modified in Electra:EIP7251]

# Activate all eligible validators
# [Modified in Electra:EIP7251]
activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
for validator in state.validators:
if is_eligible_for_activation(state, validator):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def run_test_activation_churn_limit(spec, state):

validator_count_0 = len(state.validators)

balance = spec.MIN_ACTIVATION_BALANCE if is_post_electra(spec) else spec.MAX_EFFECTIVE_BALANCE

for i in range(mock_activations):
index = validator_count_0 + i
validator = spec.Validator(
Expand All @@ -32,10 +34,10 @@ def run_test_activation_churn_limit(spec, state):
activation_epoch=spec.FAR_FUTURE_EPOCH,
exit_epoch=spec.FAR_FUTURE_EPOCH,
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
effective_balance=spec.MAX_EFFECTIVE_BALANCE,
effective_balance=balance,
)
state.validators.append(validator)
state.balances.append(spec.MAX_EFFECTIVE_BALANCE)
state.balances.append(balance)
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
from eth2spec.test.context import (
spec_state_test,
with_electra_and_later,
with_presets,
spec_test,
single_phase,
with_custom_state,
scaled_churn_balances_exceed_activation_exit_churn_limit,
default_activation_threshold,
)
from eth2spec.test.helpers.deposits import prepare_pending_deposit
from eth2spec.test.helpers.state import (
next_epoch_with_full_participation,
advance_finality_to,
set_full_participation,
)
from eth2spec.test.helpers.constants import MINIMAL


def run_process_pending_deposits(spec, state):
Expand Down Expand Up @@ -488,3 +495,26 @@ def test_process_pending_deposits_withdrawable_validator_not_churned(spec, state
assert state.pending_deposits == [
prepare_pending_deposit(spec, validator_index=1, amount=amount)
]


@with_electra_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_process_pending_deposits_scaled_churn(spec, state):
index = 0
amount = spec.get_activation_exit_churn_limit(state)
state.pending_deposits.append(
prepare_pending_deposit(spec, index, amount)
)
pre_balance = state.balances[index]

yield from run_process_pending_deposits(spec, state)

assert state.balances[index] == pre_balance + amount
assert state.deposit_balance_to_consume == 0
assert state.pending_deposits == []
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
from eth2spec.test.helpers.state import (
next_epoch_with_full_participation,
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_compounding_withdrawal_credential_with_balance,
)

# ***********************
# * CONSOLIDATION TESTS *
Expand Down Expand Up @@ -302,3 +306,131 @@ def test_pending_consolidation_with_pending_deposit(spec, state):
# Pending deposit to the source was not processed.
# It should only be processed in the next epoch transition
assert state.pending_deposits == [pending_deposit]


@with_electra_and_later
@spec_state_test
def test_pending_consolidation_source_balance_less_than_max_effective(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 withdrawable epoch to current epoch to allow processing
state.validators[source_index].withdrawable_epoch = current_epoch
# Set source and target withdrawal credential to eth1
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set the source balance to be less than effective_balance
pre_balance_source = state.validators[source_index].effective_balance - spec.EFFECTIVE_BALANCE_INCREMENT // 8
state.balances[source_index] = pre_balance_source
jtraglia marked this conversation as resolved.
Show resolved Hide resolved

pre_balance_target = state.balances[target_index]

assert state.balances[source_index] < spec.get_max_effective_balance(state.validators[source_index])

yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")

# Pending consolidation was successfully processed
assert state.balances[target_index] == pre_balance_target + pre_balance_source
assert state.balances[source_index] == 0
assert state.pending_consolidations == []


@with_electra_and_later
@spec_state_test
def test_pending_consolidation_source_balance_greater_than_max_effective(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 withdrawable epoch to current epoch to allow processing
state.validators[source_index].withdrawable_epoch = current_epoch
# Set source and target withdrawal credential to eth1
set_eth1_withdrawal_credential_with_balance(spec, state, source_index)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
# Set the source balance to be greater than effective_balance
excess_source_balance = spec.EFFECTIVE_BALANCE_INCREMENT // 8
pre_balance_source = state.validators[source_index].effective_balance + excess_source_balance
state.balances[source_index] = pre_balance_source

pre_balance_target = state.balances[target_index]

source_max_effective_balance = spec.get_max_effective_balance(state.validators[source_index])
assert state.balances[source_index] > source_max_effective_balance

yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")

# Pending consolidation was successfully processed
assert state.balances[target_index] == pre_balance_target + source_max_effective_balance
assert state.balances[source_index] == excess_source_balance
assert state.pending_consolidations == []


@with_electra_and_later
@spec_state_test
def test_pending_consolidation_source_balance_less_than_max_effective_compounding(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 withdrawable epoch to current epoch to allow processing
state.validators[source_index].withdrawable_epoch = current_epoch
# Set source and target withdrawal credential to compounding
set_compounding_withdrawal_credential_with_balance(spec, state, source_index)
set_compounding_withdrawal_credential_with_balance(spec, state, target_index)
# Set the source balance to be less than effective_balance
pre_balance_source = state.validators[source_index].effective_balance - spec.EFFECTIVE_BALANCE_INCREMENT // 8
state.balances[source_index] = pre_balance_source

pre_balance_target = state.balances[target_index]

assert state.balances[source_index] < spec.get_max_effective_balance(state.validators[source_index])

yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")

# Pending consolidation was successfully processed
assert state.balances[target_index] == pre_balance_target + pre_balance_source
assert state.balances[source_index] == 0
assert state.pending_consolidations == []


@with_electra_and_later
@spec_state_test
def test_pending_consolidation_source_balance_greater_than_max_effective_compounding(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 withdrawable epoch to current epoch to allow processing
state.validators[source_index].withdrawable_epoch = current_epoch
# Set source and target withdrawal credential to compounding
set_compounding_withdrawal_credential_with_balance(spec, state, source_index)
set_compounding_withdrawal_credential_with_balance(spec, state, target_index)
# Set the source balance to be greater than effective_balance
excess_source_balance = spec.EFFECTIVE_BALANCE_INCREMENT // 8
pre_balance_source = state.validators[source_index].effective_balance + excess_source_balance
state.balances[source_index] = pre_balance_source

pre_balance_target = state.balances[target_index]

source_max_effective_balance = spec.get_max_effective_balance(state.validators[source_index])
assert state.balances[source_index] > source_max_effective_balance

yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")

# Pending consolidation was successfully processed
assert state.balances[target_index] == pre_balance_target + source_max_effective_balance
assert state.balances[source_index] == excess_source_balance
assert state.pending_consolidations == []
jtraglia marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from eth2spec.test.helpers.deposits import mock_deposit
from eth2spec.test.helpers.state import next_epoch
from eth2spec.test.context import spec_state_test, with_electra_and_later
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_compounding_withdrawal_credential_with_balance
)


def run_test_activation_queue_eligibility(spec, state, validator_index, balance):
# move past first two irregular epochs wrt finality
next_epoch(spec, state)
next_epoch(spec, state)

state.balances[validator_index] = balance
state.validators[validator_index].effective_balance = balance

# ready for entrance into activation queue
mock_deposit(spec, state, validator_index)

yield from run_epoch_processing_with(spec, state, 'process_registry_updates')

# validator moved into activation queue if eligible
validator = state.validators[validator_index]
if validator.effective_balance < spec.MIN_ACTIVATION_BALANCE:
assert validator.activation_eligibility_epoch == spec.FAR_FUTURE_EPOCH
else:
assert validator.activation_eligibility_epoch < spec.FAR_FUTURE_EPOCH


@with_electra_and_later
@spec_state_test
def test_activation_queue_eligibility__less_than_min_activation_balance(spec, state):
index = 3
balance = spec.MIN_ACTIVATION_BALANCE - spec.EFFECTIVE_BALANCE_INCREMENT
yield from run_test_activation_queue_eligibility(spec, state, index, balance)


@with_electra_and_later
@spec_state_test
def test_activation_queue_eligibility__min_activation_balance(spec, state):
index = 5
balance = spec.MIN_ACTIVATION_BALANCE
yield from run_test_activation_queue_eligibility(spec, state, index, balance)


@with_electra_and_later
@spec_state_test
def test_activation_queue_eligibility__min_activation_balance_eth1_creds(spec, state):
index = 7
balance = spec.MIN_ACTIVATION_BALANCE
set_eth1_withdrawal_credential_with_balance(spec, state, index)
yield from run_test_activation_queue_eligibility(spec, state, index, balance)


@with_electra_and_later
@spec_state_test
def test_activation_queue_eligibility__min_activation_balance_compounding_creds(spec, state):
index = 11
balance = spec.MIN_ACTIVATION_BALANCE
set_compounding_withdrawal_credential_with_balance(spec, state, index)
yield from run_test_activation_queue_eligibility(spec, state, index, balance)


@with_electra_and_later
@spec_state_test
def test_activation_queue_eligibility__greater_than_min_activation_balance(spec, state):
index = 13
balance = spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT
set_compounding_withdrawal_credential_with_balance(spec, state, index)
yield from run_test_activation_queue_eligibility(spec, state, index, balance)
1 change: 1 addition & 0 deletions tests/generators/epoch_processing/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
_new_electra_mods_1 = {key: 'eth2spec.test.electra.epoch_processing.test_process_' + key for key in [
'effective_balance_updates',
'pending_consolidations',
'registry_updates',
]}
# This is a trick to allow tests be split into multiple files and use the same test format.
_new_electra_mods_2 = {key: 'eth2spec.test.electra.epoch_processing.' + key for key in [
Expand Down