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

In-protocol consolidation - target not liable #9

Conversation

dapplion
Copy link
Collaborator

@dapplion dapplion commented Aug 1, 2023

Extends MaxEB #3 to allow in-protocol consolidation.

A Consolidation operation moves all balance from validator source to validator target without leaving the active set. This operation is authorized by the source withdrawal credentials, and the target validating key.

Slashing liability

In this implementation

  • source is liable for target's slashable offenses before the consolidation event
  • target is not liable for source's slashable offenses before the consolidation event

Rationale

To get around the binary nature of slashed status, if the source validator is slashed, it is "un-consolidated" from the target validator. The un-consolidated validator transitions from exited to slashed and active. It will not participate in duties but it will re-use the existing validator record to track exit queue churn and correlated penalties

@dapplion dapplion force-pushed the maxeb-consolidation branch from a9a4eea to 036f5dc Compare August 1, 2023 22:26
@dapplion dapplion force-pushed the maxeb-consolidation branch from 036f5dc to ee0bda4 Compare August 1, 2023 22:27
target_validator = state[consolidation.target_index]
source_validator = state[consolidation.source_index]

assert is_active_validator(target_validator) and not target_validator.slashed and not is_consolidated(target_validator)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these conditions for the target validator may not be required but look sensible on a first approach

source_validator = state[consolidation.source_index]

assert is_active_validator(target_validator) and not target_validator.slashed and not is_consolidated(target_validator)
assert not source_validator.slashed and not is_consolidated(source_validator)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Must not be slashed otherwise the stake can escape latter correlated penalties.
  • Cannot be consolidated twice


# verify source withdrawal credentials, which have authority over validating keys
assert source_validator.withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX
assert source_validator.withdrawal_credentials[12:] == consolidation.source_address
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A mechanism similar to EIP-7002 should add the consolidation message on-chain, setting msg.sender to source address. A consolidation operation is only available to validators with 0x01 credentials.

# Fork-agnostic domain since address changes are valid across forks
domain = compute_domain(DOMAIN_CONSOLIDATION, genesis_validators_root=state.genesis_validators_root)
signing_root = compute_signing_root(source_validator.pubkey, domain)
assert bls.Verify(target_validator.pubkey, signing_root, consolidation.target_signature)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Target validator authorizes the consolidation. Since the target validator is not liable for source's actions this check may not be strictly necessary but sounds sensible of a first approach. Target validator signs over the public key of the source validator, not the index to prevent replay if source's validator index is ever re-used. There may be some other replay edge cases not covered by the current signed message

source_validator.consolidated_balance = consolidated_balance
# Balance is not exiting the active set, do not apply churn
source_validator.exit_epoch = get_current_epoch(state)
source_validator.withdrawable_epoch = Epoch(source_validator.exit_epoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source validator record with 0 balance should exit the validator set immediately. Otherwise it will be ejected on the next epoch transition. Since its balance is not exiting the validator set it can bypass the churn. withdrawable_epoch must be set sufficiently into the future to allow time to submit slashable offenses.

@dapplion
Copy link
Collaborator Author

Closing in favour of

@dapplion dapplion closed this Jan 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants