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

EIP-7002:Execution layer triggerable withdrawals #14031

Merged
merged 19 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions beacon-chain/core/electra/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//config/params:go_default_library",
Expand All @@ -33,6 +34,7 @@ go_library(
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)
Expand All @@ -46,6 +48,7 @@ go_test(
"effective_balance_updates_test.go",
"upgrade_test.go",
"validator_test.go",
"withdrawals_test.go",
],
deps = [
":go_default_library",
Expand All @@ -66,5 +69,6 @@ go_test(
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
],
)
4 changes: 4 additions & 0 deletions beacon-chain/core/electra/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconSta
if err != nil {
return nil, errors.Wrap(err, "could not process rewards and penalties")
}

state, err = ProcessRegistryUpdates(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process registry updates")
}

proportionalSlashingMultiplier, err := state.ProportionalSlashingMultiplier()
if err != nil {
return nil, err
Expand All @@ -89,6 +91,7 @@ func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconSta
if err != nil {
return nil, err
}

if err = ProcessPendingBalanceDeposits(ctx, state, primitives.Gwei(bp.ActiveCurrentEpoch)); err != nil {
return nil, err
}
Expand All @@ -98,6 +101,7 @@ func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconSta
if err := ProcessEffectiveBalanceUpdates(state); err != nil {
return nil, err
}

state, err = ProcessSlashingsReset(state)
if err != nil {
return nil, err
Expand Down
114 changes: 113 additions & 1 deletion beacon-chain/core/electra/withdrawals.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package electra

import (
"bytes"
"context"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
log "github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)

// ProcessExecutionLayerWithdrawRequests processes the validator withdrawals from the provided execution payload
Expand Down Expand Up @@ -75,6 +86,107 @@ import (
// withdrawable_epoch=withdrawable_epoch,
// ))
func ProcessExecutionLayerWithdrawRequests(ctx context.Context, st state.BeaconState, wrs []*enginev1.ExecutionLayerWithdrawalRequest) (state.BeaconState, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In the spec the function is called process_execution_layer_withdrawal_request (Withdraw --> Withdrawal). The parameter also has Withdrawal in the name,

//TODO: replace with real implementation
ctx, span := trace.StartSpan(ctx, "electra.ProcessExecutionLayerWithdrawRequests")
defer span.End()
currentEpoch := slots.ToEpoch(st.Slot())
for _, wr := range wrs {
if wr == nil {
return nil, errors.New("nil execution layer withdrawal request")
}
amount := wr.Amount
isFullExitRequest := amount == params.BeaconConfig().FullExitRequestAmount
// If partial withdrawal queue is full, only full exits are processed
if n, err := st.NumPendingPartialWithdrawals(); err != nil {
return nil, err
} else if n == params.BeaconConfig().PendingPartialWithdrawalsLimit && !isFullExitRequest {
// if the PendingPartialWithdrawalsLimit is met, the user would have paid for a partial withdrawal that's not included
log.Debugln("Skipping execution layer withdrawal request, PendingPartialWithdrawalsLimit reached")
continue
}

vIdx, exists := st.ValidatorIndexByPubkey(bytesutil.ToBytes48(wr.ValidatorPubkey))
if !exists {
log.Debugln("Skipping execution layer withdrawal request, validator index not found")
james-prysm marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between Debug and Debugln?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm not sure i used debugln since i know it adds a new line not sure if debug also does

continue
james-prysm marked this conversation as resolved.
Show resolved Hide resolved
}
validator, err := st.ValidatorAtIndex(vIdx)
if err != nil {
return nil, err
}
// Verify withdrawal credentials
hasCorrectCredential := helpers.HasExecutionWithdrawalCredentials(validator)
isCorrectSourceAddress := bytes.Equal(validator.WithdrawalCredentials[12:], wr.SourceAddress)
if !hasCorrectCredential || !isCorrectSourceAddress {
log.Debugln("Skipping execution layer withdrawal request, wrong withdrawal credentials")
continue
}

// Verify the validator is active.
if !helpers.IsActiveValidator(validator, currentEpoch) {
log.Debugln("Skipping execution layer withdrawal request, validator not active")
continue
}
// Verify the validator has not yet submitted an exit.
if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
log.Debugln("Skipping execution layer withdrawal request, validator has submitted an exit already")
continue
}
// Verify the validator has been active long enough.
if currentEpoch < validator.ActivationEpoch.AddEpoch(params.BeaconConfig().ShardCommitteePeriod) {
log.Debugln("Skipping execution layer withdrawal request, validator has not been active long enough")
continue
}

pendingBalanceToWithdraw, err := st.PendingBalanceToWithdraw(vIdx)
if err != nil {
return nil, err
}
if isFullExitRequest {
// Only exit validator if it has no pending withdrawals in the queue
if pendingBalanceToWithdraw == 0 {
maxExitEpoch, churn := validators.MaxExitEpochAndChurn(st)
Copy link
Member

Choose a reason for hiding this comment

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

Could be defined outside of the for loop

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so, if there are multiple values the loop updates the state I think

Copy link
Member

Choose a reason for hiding this comment

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

Ah. Good point

var err error
st, _, err = validators.InitiateValidatorExit(ctx, st, vIdx, maxExitEpoch, churn)
if err != nil {
return nil, err
}
}
continue
}

hasSufficientEffectiveBalance := validator.EffectiveBalance >= params.BeaconConfig().MinActivationBalance
vBal, err := st.BalanceAtIndex(vIdx)
if err != nil {
return nil, err
}
hasExcessBalance := vBal > params.BeaconConfig().MinActivationBalance+pendingBalanceToWithdraw

// Only allow partial withdrawals with compounding withdrawal credentials
if helpers.HasCompoundingWithdrawalCredential(validator) && hasSufficientEffectiveBalance && hasExcessBalance {
//to_withdraw = min(
// state.balances[index] - MIN_ACTIVATION_BALANCE - pending_balance_to_withdraw,
// amount
//)
james-prysm marked this conversation as resolved.
Show resolved Hide resolved

// note: you can safely subtract these values because haxExcessBalance is checked
toWithdraw := min(vBal-params.BeaconConfig().MinActivationBalance-pendingBalanceToWithdraw, amount)
exitQueueEpoch, err := st.ExitEpochAndUpdateChurn(primitives.Gwei(toWithdraw))
if err != nil {
return nil, err
}
// safe add the uint64 to avoid overflow
withdrawableEpoch, err := exitQueueEpoch.SafeAddEpoch(params.BeaconConfig().MinValidatorWithdrawabilityDelay)
if err != nil {
return nil, errors.Wrap(err, "failed to add withdrawablility delay to exit queue")
james-prysm marked this conversation as resolved.
Show resolved Hide resolved
}
if err := st.AppendPendingPartialWithdrawal(&ethpb.PendingPartialWithdrawal{
Index: vIdx,
Amount: toWithdraw,
WithdrawableEpoch: withdrawableEpoch,
}); err != nil {
return nil, err
}
}
}
return st, nil
}
Loading
Loading