Merge pull request #3918 from mkalinin/consolidation-switch-to-comp
This commit is contained in:
commit
f081b1b5c2
|
@ -99,6 +99,7 @@
|
|||
- [Deposit requests](#deposit-requests)
|
||||
- [New `process_deposit_request`](#new-process_deposit_request)
|
||||
- [Execution layer consolidation requests](#execution-layer-consolidation-requests)
|
||||
- [New `is_valid_switch_to_compounding_request`](#new-is_valid_switch_to_compounding_request)
|
||||
- [New `process_consolidation_request`](#new-process_consolidation_request)
|
||||
- [Testing](#testing)
|
||||
|
||||
|
@ -627,9 +628,8 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
|
|||
```python
|
||||
def switch_to_compounding_validator(state: BeaconState, index: ValidatorIndex) -> None:
|
||||
validator = state.validators[index]
|
||||
if has_eth1_withdrawal_credential(validator):
|
||||
validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
queue_excess_active_balance(state, index)
|
||||
validator.withdrawal_credentials = COMPOUNDING_WITHDRAWAL_PREFIX + validator.withdrawal_credentials[1:]
|
||||
queue_excess_active_balance(state, index)
|
||||
```
|
||||
|
||||
#### New `queue_excess_active_balance`
|
||||
|
@ -858,9 +858,6 @@ def process_pending_consolidations(state: BeaconState) -> None:
|
|||
if source_validator.withdrawable_epoch > next_epoch:
|
||||
break
|
||||
|
||||
# Churn any target excess active balance of target and raise its max
|
||||
switch_to_compounding_validator(state, pending_consolidation.target_index)
|
||||
|
||||
# Calculate the consolidated balance
|
||||
max_effective_balance = get_max_effective_balance(source_validator)
|
||||
source_effective_balance = min(state.balances[pending_consolidation.source_index], max_effective_balance)
|
||||
|
@ -1217,14 +1214,6 @@ def apply_deposit(state: BeaconState,
|
|||
state.pending_balance_deposits.append(
|
||||
PendingBalanceDeposit(index=index, amount=amount)
|
||||
) # [Modified in Electra:EIP7251]
|
||||
# Check if valid deposit switch to compounding credentials
|
||||
if (
|
||||
is_compounding_withdrawal_credential(withdrawal_credentials)
|
||||
and has_eth1_withdrawal_credential(state.validators[index])
|
||||
and is_valid_deposit_signature(pubkey, withdrawal_credentials, amount, signature)
|
||||
):
|
||||
switch_to_compounding_validator(state, index)
|
||||
|
||||
```
|
||||
|
||||
###### New `is_valid_deposit_signature`
|
||||
|
@ -1396,6 +1385,45 @@ def process_deposit_request(state: BeaconState, deposit_request: DepositRequest)
|
|||
|
||||
##### Execution layer consolidation requests
|
||||
|
||||
###### New `is_valid_switch_to_compounding_request`
|
||||
|
||||
```python
|
||||
def is_valid_switch_to_compounding_request(
|
||||
state: BeaconState,
|
||||
consolidation_request: ConsolidationRequest
|
||||
) -> bool:
|
||||
# Switch to compounding requires source and target be equal
|
||||
if consolidation_request.source_pubkey != consolidation_request.target_pubkey:
|
||||
return False
|
||||
|
||||
# Verify pubkey exists
|
||||
source_pubkey = consolidation_request.source_pubkey
|
||||
validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
if source_pubkey not in validator_pubkeys:
|
||||
return False
|
||||
|
||||
source_validator = state.validators[ValidatorIndex(validator_pubkeys.index(source_pubkey))]
|
||||
|
||||
# Verify request has been authorized
|
||||
if source_validator.withdrawal_credentials[12:] != consolidation_request.source_address:
|
||||
return False
|
||||
|
||||
# Verify source withdrawal credentials
|
||||
if not has_eth1_withdrawal_credential(source_validator):
|
||||
return False
|
||||
|
||||
# Verify the source is active
|
||||
current_epoch = get_current_epoch(state)
|
||||
if not is_active_validator(source_validator, current_epoch):
|
||||
return False
|
||||
|
||||
# Verify exit for source has not been initiated
|
||||
if source_validator.exit_epoch != FAR_FUTURE_EPOCH:
|
||||
return False
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
###### New `process_consolidation_request`
|
||||
|
||||
```python
|
||||
|
@ -1403,6 +1431,16 @@ def process_consolidation_request(
|
|||
state: BeaconState,
|
||||
consolidation_request: ConsolidationRequest
|
||||
) -> None:
|
||||
if is_valid_switch_to_compounding_request(state, consolidation_request):
|
||||
validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
request_source_pubkey = consolidation_request.source_pubkey
|
||||
source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey))
|
||||
switch_to_compounding_validator(state, source_index)
|
||||
return
|
||||
|
||||
# Verify that source != target, so a consolidation cannot be used as an exit.
|
||||
if consolidation_request.source_pubkey == consolidation_request.target_pubkey:
|
||||
return
|
||||
# If the pending consolidations queue is full, consolidation requests are ignored
|
||||
if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT:
|
||||
return
|
||||
|
@ -1423,10 +1461,6 @@ def process_consolidation_request(
|
|||
source_validator = state.validators[source_index]
|
||||
target_validator = state.validators[target_index]
|
||||
|
||||
# Verify that source != target, so a consolidation cannot be used as an exit.
|
||||
if source_index == target_index:
|
||||
return
|
||||
|
||||
# Verify source withdrawal credentials
|
||||
has_correct_credential = has_execution_withdrawal_credential(source_validator)
|
||||
is_correct_source_address = (
|
||||
|
@ -1462,6 +1496,10 @@ def process_consolidation_request(
|
|||
source_index=source_index,
|
||||
target_index=target_index
|
||||
))
|
||||
|
||||
# Churn any target excess active balance of target and raise its max
|
||||
if has_eth1_withdrawal_credential(target_validator):
|
||||
switch_to_compounding_validator(state, target_index)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
|
|
@ -66,6 +66,106 @@ def test_basic_consolidation_in_current_consolidation_epoch(spec, state):
|
|||
assert state.validators[source_index].exit_epoch == expected_exit_epoch
|
||||
|
||||
|
||||
@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_basic_consolidation_with_excess_target_balance(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]
|
||||
|
||||
# Set source to eth1 credentials
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Make consolidation with source address
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[target_index].pubkey,
|
||||
)
|
||||
|
||||
# Set target to eth1 credentials
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, target_index)
|
||||
|
||||
# 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
|
||||
|
||||
# Add excess balance
|
||||
state.balances[target_index] = state.balances[target_index] + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
yield from run_consolidation_processing(spec, state, consolidation)
|
||||
|
||||
# Check consolidation churn is decremented correctly
|
||||
assert (
|
||||
state.consolidation_balance_to_consume
|
||||
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
|
||||
)
|
||||
# Check exit epoch
|
||||
assert state.validators[source_index].exit_epoch == expected_exit_epoch
|
||||
|
||||
|
||||
@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_basic_consolidation_with_excess_target_balance_and_compounding_credentials(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]
|
||||
|
||||
# Set source to eth1 credentials
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Make consolidation with source address
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[target_index].pubkey,
|
||||
)
|
||||
|
||||
# Set target to eth1 credentials
|
||||
set_compounding_withdrawal_credential(spec, state, target_index)
|
||||
|
||||
# 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
|
||||
|
||||
# Add excess balance
|
||||
state.balances[target_index] = state.balances[target_index] + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
yield from run_consolidation_processing(spec, state, consolidation)
|
||||
|
||||
# Check consolidation churn is decremented correctly
|
||||
assert (
|
||||
state.consolidation_balance_to_consume
|
||||
== consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE
|
||||
)
|
||||
# Check exit epoch
|
||||
assert state.validators[source_index].exit_epoch == expected_exit_epoch
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
|
||||
@with_custom_state(
|
||||
|
@ -235,7 +335,7 @@ def test_basic_consolidation_with_compounding_credentials(spec, state):
|
|||
target_pubkey=state.validators[target_index].pubkey,
|
||||
)
|
||||
|
||||
# Set target to eth1 credentials
|
||||
# Set target to compounding credentials
|
||||
set_compounding_withdrawal_credential(spec, state, target_index)
|
||||
|
||||
# Set the consolidation balance to consume equal to churn limit
|
||||
|
@ -395,17 +495,9 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state):
|
|||
assert state.consolidation_balance_to_consume == expected_balance
|
||||
|
||||
|
||||
# Failing tests
|
||||
|
||||
@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_equals_target(spec, state):
|
||||
@spec_state_test
|
||||
def test_basic_switch_to_compounding(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
|
||||
|
@ -421,14 +513,67 @@ def test_incorrect_source_equals_target(spec, state):
|
|||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
# Check the the return condition
|
||||
assert consolidation.source_pubkey == consolidation.target_pubkey
|
||||
|
||||
yield from run_consolidation_processing(
|
||||
spec, state, consolidation, success=False
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=True
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_with_excess(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
|
||||
# Set source to eth1 credentials
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Add excess balance
|
||||
state.balances[source_index] = state.balances[source_index] + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
# Make consolidation from source to source
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=True
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_with_pending_consolidations_at_limit(spec, state):
|
||||
state.pending_consolidations = [
|
||||
spec.PendingConsolidation(source_index=0, target_index=1)
|
||||
] * spec.PENDING_CONSOLIDATIONS_LIMIT
|
||||
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
|
||||
# Set source to eth1 credentials
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Add excess balance
|
||||
state.balances[source_index] = state.balances[source_index] + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
# Make consolidation from source to source
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=True
|
||||
)
|
||||
|
||||
|
||||
# Tests that should fail
|
||||
|
||||
@with_electra_and_later
|
||||
@with_presets([MINIMAL], "need sufficient consolidation churn limit")
|
||||
@with_custom_state(
|
||||
|
@ -797,6 +942,155 @@ def test_incorrect_unknown_target_pubkey(spec, state):
|
|||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_exited_source(spec, state):
|
||||
# Set up an otherwise correct request
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
# Initiate exit for source
|
||||
spec.initiate_validator_exit(state, source_index)
|
||||
|
||||
# Check the return condition
|
||||
assert state.validators[source_index].exit_epoch != spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_inactive_source(spec, state):
|
||||
# Set up an otherwise correct request
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
# Set source validator as not yet activated
|
||||
state.validators[source_index].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
# Check the the return condition
|
||||
assert not spec.is_active_validator(state.validators[source_index], current_epoch)
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_source_bls_withdrawal_credential(spec, state):
|
||||
# Set up a correct request, but source does have
|
||||
# a bls withdrawal credential
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=state.validators[source_index].withdrawal_credentials[12:],
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
# Check the the return condition
|
||||
assert not spec.has_eth1_withdrawal_credential(state.validators[source_index])
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_source_coumpounding_withdrawal_credential(spec, state):
|
||||
# Set up a correct request, but source does have
|
||||
# a compounding withdrawal credential and excess balance
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
source_address = b"\x22" * 20
|
||||
set_compounding_withdrawal_credential(spec, state, source_index, address=source_address)
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
state.balances[source_index] = spec.MIN_ACTIVATION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
# Check the the return condition
|
||||
assert not spec.has_eth1_withdrawal_credential(state.validators[source_index])
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_not_authorized(spec, state):
|
||||
# Set up an otherwise correct request
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Make request with different source address
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=b"\x33" * 20,
|
||||
source_pubkey=state.validators[source_index].pubkey,
|
||||
target_pubkey=state.validators[source_index].pubkey,
|
||||
)
|
||||
|
||||
# Check the the return condition
|
||||
assert not state.validators[source_index].withdrawal_credentials[12:] == consolidation.source_address
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
def test_switch_to_compounding_unknown_source_pubkey(spec, state):
|
||||
# Set up an otherwise correct request
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
source_index = spec.get_active_validator_indices(state, current_epoch)[0]
|
||||
source_address = b"\x22" * 20
|
||||
set_eth1_withdrawal_credential_with_balance(
|
||||
spec, state, source_index, address=source_address
|
||||
)
|
||||
# Make consolidation with different source pubkey
|
||||
consolidation = spec.ConsolidationRequest(
|
||||
source_address=source_address,
|
||||
source_pubkey=b"\x00" * 48,
|
||||
target_pubkey=b"\x00" * 48,
|
||||
)
|
||||
|
||||
# Check the the return condition
|
||||
assert not state.validators[source_index].pubkey == consolidation.source_pubkey
|
||||
|
||||
yield from run_switch_to_compounding_processing(
|
||||
spec, state, consolidation, success=False
|
||||
)
|
||||
|
||||
|
||||
def run_consolidation_processing(spec, state, consolidation, success=True):
|
||||
"""
|
||||
Run ``process_consolidation``, yielding:
|
||||
|
@ -805,7 +1099,6 @@ def run_consolidation_processing(spec, state, consolidation, success=True):
|
|||
- post-state ('post').
|
||||
If ``success == False``, ``process_consolidation_request`` would return without any state change.
|
||||
"""
|
||||
|
||||
if success:
|
||||
validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey))
|
||||
|
@ -815,6 +1108,8 @@ def run_consolidation_processing(spec, state, consolidation, success=True):
|
|||
pre_exit_epoch_source = source_validator.exit_epoch
|
||||
pre_exit_epoch_target = target_validator.exit_epoch
|
||||
pre_pending_consolidations = state.pending_consolidations.copy()
|
||||
pre_target_withdrawal_credentials = target_validator.withdrawal_credentials
|
||||
pre_target_balance = state.balances[target_index]
|
||||
else:
|
||||
pre_state = state.copy()
|
||||
|
||||
|
@ -826,9 +1121,10 @@ def run_consolidation_processing(spec, state, consolidation, success=True):
|
|||
yield 'post', state
|
||||
|
||||
if success:
|
||||
# Check source and target have execution credentials
|
||||
# Check source has execution credentials
|
||||
assert spec.has_execution_withdrawal_credential(source_validator)
|
||||
assert spec.has_execution_withdrawal_credential(target_validator)
|
||||
# Check target has compounding credentials
|
||||
assert spec.has_compounding_withdrawal_credential(state.validators[target_index])
|
||||
# Check source address in the consolidation fits the withdrawal credentials
|
||||
assert source_validator.withdrawal_credentials[12:] == consolidation.source_address
|
||||
# Check source and target are not the same
|
||||
|
@ -840,11 +1136,74 @@ def run_consolidation_processing(spec, state, consolidation, success=True):
|
|||
assert state.validators[source_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
# Check that the exit epoch matches earliest_consolidation_epoch
|
||||
assert state.validators[source_index].exit_epoch == state.earliest_consolidation_epoch
|
||||
# Check that the withdrawable_epoch is set correctly
|
||||
assert state.validators[source_index].withdrawable_epoch == (
|
||||
state.validators[source_index].exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
)
|
||||
# Check that the correct consolidation has been appended
|
||||
expected_new_pending_consolidation = spec.PendingConsolidation(
|
||||
source_index=source_index,
|
||||
target_index=target_index,
|
||||
)
|
||||
assert state.pending_consolidations == pre_pending_consolidations + [expected_new_pending_consolidation]
|
||||
# Check excess balance is queued if the target switched to compounding
|
||||
if pre_target_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX:
|
||||
assert state.validators[target_index].withdrawal_credentials == (
|
||||
spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_target_withdrawal_credentials[1:])
|
||||
assert state.balances[target_index] == spec.MIN_ACTIVATION_BALANCE
|
||||
if pre_target_balance > spec.MIN_ACTIVATION_BALANCE:
|
||||
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(
|
||||
index=target_index, amount=(pre_target_balance - spec.MIN_ACTIVATION_BALANCE))]
|
||||
else:
|
||||
assert state.balances[target_index] == pre_target_balance
|
||||
else:
|
||||
assert pre_state == state
|
||||
|
||||
|
||||
def run_switch_to_compounding_processing(spec, state, consolidation, success=True):
|
||||
"""
|
||||
Run ``process_consolidation``, yielding:
|
||||
- pre-state ('pre')
|
||||
- consolidation_request ('consolidation_request')
|
||||
- post-state ('post').
|
||||
If ``success == False``, ``process_consolidation_request`` would return without any state change.
|
||||
"""
|
||||
if success:
|
||||
validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
source_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.source_pubkey))
|
||||
target_index = spec.ValidatorIndex(validator_pubkeys.index(consolidation.target_pubkey))
|
||||
source_validator = state.validators[source_index]
|
||||
pre_pending_consolidations = state.pending_consolidations.copy()
|
||||
pre_withdrawal_credentials = source_validator.withdrawal_credentials
|
||||
pre_balance = state.balances[source_index]
|
||||
else:
|
||||
pre_state = state.copy()
|
||||
|
||||
yield 'pre', state
|
||||
yield 'consolidation_request', consolidation
|
||||
|
||||
spec.process_consolidation_request(state, consolidation)
|
||||
|
||||
yield 'post', state
|
||||
|
||||
if success:
|
||||
# Check that source and target are same
|
||||
assert source_index == target_index
|
||||
# Check that the credentials before the switch are of ETH1 type
|
||||
assert pre_withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
# Check source address in the consolidation fits the withdrawal credentials
|
||||
assert state.validators[source_index].withdrawal_credentials[12:] == consolidation.source_address
|
||||
# Check that the source has switched to compounding
|
||||
assert state.validators[source_index].withdrawal_credentials == (
|
||||
spec.COMPOUNDING_WITHDRAWAL_PREFIX + pre_withdrawal_credentials[1:]
|
||||
)
|
||||
# Check excess balance is queued
|
||||
assert state.balances[source_index] == spec.MIN_ACTIVATION_BALANCE
|
||||
if pre_balance > spec.MIN_ACTIVATION_BALANCE:
|
||||
assert state.pending_balance_deposits == [spec.PendingBalanceDeposit(
|
||||
index=source_index, amount=(pre_balance - spec.MIN_ACTIVATION_BALANCE))]
|
||||
# Check no consolidation has been initiated
|
||||
assert state.validators[source_index].exit_epoch == spec.FAR_FUTURE_EPOCH
|
||||
assert state.pending_consolidations == pre_pending_consolidations
|
||||
else:
|
||||
assert pre_state == state
|
||||
|
|
|
@ -36,10 +36,6 @@ def test_basic_pending_consolidation(spec, state):
|
|||
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 == []
|
||||
|
@ -65,9 +61,6 @@ def test_consolidation_not_yet_withdrawable_validator(spec, state):
|
|||
|
||||
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")
|
||||
|
||||
|
@ -75,11 +68,6 @@ def test_consolidation_not_yet_withdrawable_validator(spec, state):
|
|||
# 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
|
||||
|
||||
|
@ -121,17 +109,9 @@ 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_electra_and_later
|
||||
|
@ -167,26 +147,14 @@ def test_all_consolidation_cases_together(spec, state):
|
|||
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
|
||||
|
@ -226,22 +194,11 @@ def test_pending_consolidation_future_epoch(spec, state):
|
|||
|
||||
# Pending consolidation was successfully processed
|
||||
expected_source_balance = state_before_consolidation.balances[source_index] - spec.MIN_ACTIVATION_BALANCE
|
||||
assert (
|
||||
state.validators[target_index].withdrawal_credentials[:1]
|
||||
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
|
||||
)
|
||||
assert state.balances[target_index] == 2 * spec.MIN_ACTIVATION_BALANCE
|
||||
expected_target_balance = state_before_consolidation.balances[target_index] + spec.MIN_ACTIVATION_BALANCE
|
||||
assert state.balances[source_index] == expected_source_balance
|
||||
assert state.balances[target_index] == expected_target_balance
|
||||
assert state.pending_consolidations == []
|
||||
|
||||
# Pending balance deposit to the target is created as part of `switch_to_compounding_validator`.
|
||||
# The excess balance to queue are the rewards accumulated over the previous epoch transitions.
|
||||
expected_pending_balance = state_before_consolidation.balances[target_index] - spec.MIN_ACTIVATION_BALANCE
|
||||
assert len(state.pending_balance_deposits) > 0
|
||||
pending_balance_deposit = state.pending_balance_deposits[len(state.pending_balance_deposits) - 1]
|
||||
assert pending_balance_deposit.index == target_index
|
||||
assert pending_balance_deposit.amount == expected_pending_balance
|
||||
|
||||
|
||||
@with_electra_and_later
|
||||
@spec_state_test
|
||||
|
@ -280,10 +237,6 @@ def test_pending_consolidation_compounding_creds(spec, state):
|
|||
expected_target_balance = (
|
||||
state_before_consolidation.balances[source_index] + state_before_consolidation.balances[target_index]
|
||||
)
|
||||
assert (
|
||||
state.validators[target_index].withdrawal_credentials[:1]
|
||||
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
|
||||
)
|
||||
assert state.balances[target_index] == expected_target_balance
|
||||
# All source balance is active and moved to the target,
|
||||
# because the source validator has compounding credentials
|
||||
|
@ -336,10 +289,6 @@ def test_pending_consolidation_with_pending_deposit(spec, state):
|
|||
expected_target_balance = (
|
||||
state_before_consolidation.balances[source_index] + state_before_consolidation.balances[target_index]
|
||||
)
|
||||
assert (
|
||||
state.validators[target_index].withdrawal_credentials[:1]
|
||||
== spec.COMPOUNDING_WITHDRAWAL_PREFIX
|
||||
)
|
||||
assert state.balances[target_index] == expected_target_balance
|
||||
assert state.balances[source_index] == 0
|
||||
assert state.pending_consolidations == []
|
||||
|
|
Loading…
Reference in New Issue