-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Add EIP: Prevent using consolidations as withdrawals #10656
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
base: master
Are you sure you want to change the base?
Add EIP: Prevent using consolidations as withdrawals #10656
Conversation
File
|
|
The commit 0f612a4 (as a parent of 1674f64) contains errors. |
|
The proposal sounds good, though canceling consolidation altogether might harm its UX. I would rather add a fee that scales exponentially with the excess balance that results from the consolidation. This will add forgiveness for the small excess balances and prevent using consolidations as a withdrawal tool |
|
@mkalinin please update |
|
My thinking is - if it's not breaking weak subjectivity parameters or other security concerns (I don't think it does), there's no strong enough reason to change it. Current situation makes sophisticated participant better off than less capitalized or skilled ones, which is bad, but the change will make some things break and require error-handling upgrades for people who do consolidations (esp costly in onchain protocols that adopt consolidations), which is also bad. |
|
I guess the root cause is that some part of the source validator’s balance effectively skips the normal exit queue process. And one way to tackle this directly is to treat that leftover balance (the portion that can’t fit on the target) as if it were exiting normally — by keeping it in the regular exit queue. That can be done by increasing the Smth like: def process_pending_consolidations(state: BeaconState) -> None:
next_epoch = Epoch(get_current_epoch(state) + 1)
next_pending_consolidation = 0
for pending_consolidation in state.pending_consolidations:
source_validator = state.validators[pending_consolidation.source_index]
if source_validator.slashed:
next_pending_consolidation += 1
continue
if source_validator.withdrawable_epoch > next_epoch:
break
# Calculate the maximum stake that can be received by the target validator
movable_balance_limit = max(0, MAX_EFFECTIVE_BALANCE_ELECTRA - state.balances[pending_consolidation.target_index])
# Calculate the amount of balance that can actually be transferred
movable_balance = min(state.balances[pending_consolidation.source_index], movable_balance_limit)
# Transfer the movable balance to the target validator; any excess remains with the source
decrease_balance(state, pending_consolidation.source_index, movable_balance)
increase_balance(state, pending_consolidation.target_index, movable_balance)
remaining_balance = state.balances[pending_consolidation.source_index]
if remaining_balance > 0:
# Consume the regular exit queue churn for the remaining balance
exit_queue_epoch = compute_exit_epoch_and_update_churn(state, remaining_balance)
# No need to add MIN_VALIDATOR_WITHDRAWABLE_DELAY since the validator has already passed that delay
source_validator.withdrawable_epoch = exit_queue_epoch
next_pending_consolidation += 1
state.pending_consolidations = state.pending_consolidations[next_pending_consolidation:]I guess this shouldn’t break any protocol invariants, increasing the |
This is interesting! The way to do it would be simply capping the balance that is moved such that there is no excess on the target, the rest will stay at the source’s balance and wait for the exit. One potential issue: consolidation contract can be used to trigger full exits now when there is an imbalance between exit contract’s queue and the consolidation contract’s one. |
The problem with this solution is the waste of the consolidation queue as when consolidation request is processed entire balance of the source is subtracted from the consolidation queue and then upon processing pending consolidation the remainder should also hit the exit queue. A solution to this problem would be in splitting the source balance between exit and consolidation queues upon processing consolidation request which is additional complexity and double semantics of the consolidation request which isn’t good from the design perspective. |
Agree. I also noted that it might actually be important to fix the fact that exit_epoch is assigned based on the consolidation queue when the balance does not fit. Getting an earlier exit_epoch could theoretically be exploited — since it allows a validator to stop performing its duties sooner.
Yeah, also thought about it and sketched this. I don't like this idea either, but since it's been mentioned... def process_consolidation_request(
state: BeaconState, consolidation_request: ConsolidationRequest
) -> None:
# ...
expected_target_validator_balance = min(
get_max_effective_balance(target_validator),
target_validator.effective_balance + get_pending_balance_to_consolidate(state, target_index)
)
expected_shortage_in_target_balance = get_max_effective_balance(target_validator) - expected_target_validator_balance
expected_balance_to_consolidate = min(expected_shortage_in_target_balance, source_validator.effective_balance)
expected_remaining_balance = source_validator.effective_balance - expected_balance_to_consolidate
# Consume the consolidation queue churn
source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
state, expected_balance_to_consolidate
)
if expected_remaining_balance > 0:
# Consume the exit queue churn for the remaining balance
exit_queue_epoch = compute_exit_epoch_and_update_churn(state, expected_remaining_balance)
exit_consolidation_epoch = source_validator.exit_epoch
# Use intermediate epoch between exit_consolidation_epoch and exit_queue_epoch
# depending on the proportion of balances in each queue
#
# exit_consolidation_epoch exit_queue_epoch
# ---|---------------------------------------|---->
# ^
# exit_epoch
#
# exit_epoch - exit_consolidation_epoch expected_remaining_balance
# ------------------------------------------- = --------------------------
# exit_queue_epoch - exit_consolidation_epoch effective_balance
#
if exit_queue_epoch > exit_consolidation_epoch:
source_validator.exit_epoch = (
exit_consolidation_epoch
+ (exit_queue_epoch - exit_consolidation_epoch)
* expected_remaining_balance // effective_balance
)
source_validator.withdrawable_epoch = Epoch(
source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
state.pending_consolidations.append(
PendingConsolidation(source_index=source_index, target_index=target_index)
) |
Co-authored-by: Andrew B Coathup <[email protected]>
Co-authored-by: Andrew B Coathup <[email protected]>
No description provided.