diff --git a/specs/_features/eip7251/beacon-chain.md b/specs/_features/eip7251/beacon-chain.md index d12516823..5e2b8888b 100644 --- a/specs/_features/eip7251/beacon-chain.md +++ b/specs/_features/eip7251/beacon-chain.md @@ -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) ``` diff --git a/tests/core/pyspec/eth2spec/test/eip7251/epoch_processing/test_process_pending_consolidations.py b/tests/core/pyspec/eth2spec/test/eip7251/epoch_processing/test_process_pending_consolidations.py index 06345f151..15ba80d74 100644 --- a/tests/core/pyspec/eth2spec/test/eip7251/epoch_processing/test_process_pending_consolidations.py +++ b/tests/core/pyspec/eth2spec/test/eip7251/epoch_processing/test_process_pending_consolidations.py @@ -18,11 +18,48 @@ def test_basic_pending_consolidation(spec, state): # 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 = 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 + pre_balances = state.balances + 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 @@ -41,6 +78,10 @@ def test_skip_consolidation_when_source_slashed(spec, state): 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 +93,48 @@ 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)] + print(spec.get_active_validator_indices(state, current_epoch)) + 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 + 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 + 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:] \ No newline at end of file