Skip to content

Commit

Permalink
Merge pull request #4000 from mkalinin/consolidation-request-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
jtraglia authored Nov 1, 2024
2 parents eb60227 + 31d4974 commit 1b408e9
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
6 changes: 6 additions & 0 deletions specs/electra/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,12 @@ def process_consolidation_request(
return
if target_validator.exit_epoch != FAR_FUTURE_EPOCH:
return
# Verify the source has been active long enough
if current_epoch < source_validator.activation_epoch + SHARD_COMMITTEE_PERIOD:
return
# Verify the source has no pending withdrawals in the queue
if get_pending_balance_to_withdraw(state, source_index) > 0:
return

# Initiate source validator exit and append pending consolidation
source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
@spec_test
@single_phase
def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -75,6 +77,8 @@ def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
@spec_test
@single_phase
def test_basic_consolidation_with_excess_target_balance(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -125,6 +129,8 @@ def test_basic_consolidation_with_excess_target_balance(spec, state):
@spec_test
@single_phase
def test_basic_consolidation_with_excess_target_balance_and_compounding_credentials(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -175,6 +181,8 @@ def test_basic_consolidation_with_excess_target_balance_and_compounding_credenti
@spec_test
@single_phase
def test_basic_consolidation_in_new_consolidation_epoch(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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
Expand Down Expand Up @@ -220,6 +228,8 @@ def test_basic_consolidation_in_new_consolidation_epoch(spec, state):
@spec_test
@single_phase
def test_basic_consolidation_with_preexisting_churn(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -267,6 +277,8 @@ def test_basic_consolidation_with_preexisting_churn(spec, state):
@spec_test
@single_phase
def test_basic_consolidation_with_insufficient_preexisting_churn(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -318,6 +330,8 @@ def test_basic_consolidation_with_insufficient_preexisting_churn(spec, state):
@spec_test
@single_phase
def test_basic_consolidation_with_compounding_credentials(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -363,6 +377,8 @@ def test_basic_consolidation_with_compounding_credentials(spec, state):
@spec_test
@single_phase
def test_consolidation_churn_limit_balance(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -411,6 +427,8 @@ def test_consolidation_churn_limit_balance(spec, state):
@spec_test
@single_phase
def test_consolidation_balance_larger_than_churn_limit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -458,6 +476,8 @@ def test_consolidation_balance_larger_than_churn_limit(spec, state):
@spec_test
@single_phase
def test_consolidation_balance_through_two_churn_epochs(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# 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]
Expand Down Expand Up @@ -498,6 +518,8 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state):
@with_electra_and_later
@spec_state_test
def test_basic_switch_to_compounding(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]

Expand Down Expand Up @@ -583,6 +605,9 @@ def test_switch_to_compounding_with_pending_consolidations_at_limit(spec, state)
@spec_test
@single_phase
def test_incorrect_exceed_pending_consolidations_limit(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
] * spec.PENDING_CONSOLIDATIONS_LIMIT
Expand Down Expand Up @@ -614,6 +639,9 @@ def test_incorrect_exceed_pending_consolidations_limit(spec, state):
@spec_state_test
@single_phase
def test_incorrect_not_enough_consolidation_churn_available(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

state.pending_consolidations = [
spec.PendingConsolidation(source_index=0, target_index=1)
]
Expand Down Expand Up @@ -651,6 +679,8 @@ def test_incorrect_not_enough_consolidation_churn_available(spec, state):
@spec_test
@single_phase
def test_incorrect_exited_source(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -686,6 +716,8 @@ def test_incorrect_exited_source(spec, state):
@spec_test
@single_phase
def test_incorrect_exited_target(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -720,6 +752,8 @@ def test_incorrect_exited_target(spec, state):
@spec_test
@single_phase
def test_incorrect_inactive_source(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -755,6 +789,8 @@ def test_incorrect_inactive_source(spec, state):
@spec_test
@single_phase
def test_incorrect_inactive_target(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -790,6 +826,8 @@ def test_incorrect_inactive_target(spec, state):
@spec_test
@single_phase
def test_incorrect_no_source_execution_withdrawal_credential(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up a correct consolidation, but source does not have
# an execution withdrawal credential
current_epoch = spec.get_current_epoch(state)
Expand Down Expand Up @@ -820,6 +858,8 @@ def test_incorrect_no_source_execution_withdrawal_credential(spec, state):
@spec_test
@single_phase
def test_incorrect_no_target_execution_withdrawal_credential(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up a correct consolidation, but target does not have
# an execution withdrawal credential
current_epoch = spec.get_current_epoch(state)
Expand Down Expand Up @@ -852,6 +892,8 @@ def test_incorrect_no_target_execution_withdrawal_credential(spec, state):
@spec_test
@single_phase
def test_incorrect_incorrect_source_address(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -885,6 +927,8 @@ def test_incorrect_incorrect_source_address(spec, state):
@spec_test
@single_phase
def test_incorrect_unknown_source_pubkey(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand Down Expand Up @@ -918,6 +962,8 @@ def test_incorrect_unknown_source_pubkey(spec, state):
@spec_test
@single_phase
def test_incorrect_unknown_target_pubkey(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
current_epoch = spec.get_current_epoch(state)
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
Expand All @@ -942,6 +988,80 @@ def test_incorrect_unknown_target_pubkey(spec, state):
)


@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_incorrect_source_has_pending_withdrawal(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for consolidation
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
# Set up an otherwise correct consolidation
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_address = b"\x22" * 20
excess_balance = spec.EFFECTIVE_BALANCE_INCREMENT // 4
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address, balance=spec.MIN_ACTIVATION_BALANCE + excess_balance
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)

# Create pending withdrawal
pending_withdrawal = spec.PendingPartialWithdrawal(
index=0, amount=excess_balance, withdrawable_epoch=current_epoch
)
state.pending_partial_withdrawals.append(pending_withdrawal)

# Check the return condition
assert spec.get_pending_balance_to_withdraw(state, source_index) > 0

yield from run_consolidation_processing(
spec, state, consolidation, success=False
)


@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_incorrect_source_not_active_long_enough(spec, state):
# Set up an otherwise correct consolidation
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_address = b"\x22" * 20
excess_balance = spec.EFFECTIVE_BALANCE_INCREMENT // 4
set_eth1_withdrawal_credential_with_balance(
spec, state, source_index, address=source_address, balance=spec.MIN_ACTIVATION_BALANCE + excess_balance
)
consolidation = spec.ConsolidationRequest(
source_address=source_address,
source_pubkey=state.validators[source_index].pubkey,
target_pubkey=state.validators[target_index].pubkey,
)
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)

# Check the return condition
assert current_epoch < state.validators[source_index].activation_epoch + spec.config.SHARD_COMMITTEE_PERIOD

yield from run_consolidation_processing(
spec, state, consolidation, success=False
)


@with_electra_and_later
@spec_state_test
def test_switch_to_compounding_exited_source(spec, state):
Expand Down

0 comments on commit 1b408e9

Please sign in to comment.