Files
prysm/beacon-chain/core/gloas/pending_payment.go
2026-01-02 22:14:49 -08:00

91 lines
3.3 KiB
Go

package gloas
import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/pkg/errors"
)
// ProcessBuilderPendingPayments processes the builder pending payments from the previous epoch.
// Spec v1.6.1 (pseudocode):
// process_builder_pending_payments(state: BeaconState) -> None:
//
// quorum = get_builder_payment_quorum_threshold(state)
// for payment in state.builder_pending_payments[:SLOTS_PER_EPOCH]:
// if payment.weight >= quorum:
// exit_epoch = compute_exit_epoch_and_update_churn(state, payment.withdrawal.amount)
// payment.withdrawal.withdrawable_epoch =
// Epoch(exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
// state.builder_pending_withdrawals.append(payment.withdrawal)
// state.builder_pending_payments =
// state.builder_pending_payments[SLOTS_PER_EPOCH:]
// + [BuilderPendingPayment() for _ in range(SLOTS_PER_EPOCH)]
func ProcessBuilderPendingPayments(state state.BeaconState) error {
quorum, err := builderQuorumThreshold(state)
if err != nil {
return errors.Wrap(err, "could not compute builder payment quorum threshold")
}
payments, err := state.BuilderPendingPayments()
if err != nil {
return errors.Wrap(err, "could not get builder pending payments")
}
cfg := params.BeaconConfig()
slotsPerEpoch := uint64(cfg.SlotsPerEpoch)
minWithdrawDelay := uint64(cfg.MinValidatorWithdrawabilityDelay)
for i := uint64(0); i < slotsPerEpoch; i++ {
payment := payments[i]
if quorum > payment.Weight {
continue
}
amount := payment.Withdrawal.Amount
exitEpoch, err := state.ExitEpochAndUpdateChurn(amount)
if err != nil {
return errors.Wrapf(err, "could not compute exit epoch for payment %d", i)
}
withdrawableEpoch, err := exitEpoch.SafeAdd(minWithdrawDelay)
if err != nil {
return errors.Wrapf(err, "could not compute withdrawable epoch for payment %d", i)
}
payment.Withdrawal.WithdrawableEpoch = withdrawableEpoch
if err := state.AppendBuilderPendingWithdrawal(payment.Withdrawal); err != nil {
return errors.Wrapf(err, "could not append builder pending withdrawal %d", i)
}
}
if err := state.RotateBuilderPendingPayments(); err != nil {
return errors.Wrap(err, "could not rotate builder pending payments")
}
return nil
}
// builderQuorumThreshold calculates the quorum threshold for builder payments.
// Spec v1.6.1 (pseudocode):
// def get_builder_payment_quorum_threshold(state: BeaconState) -> uint64:
//
// per_slot_balance = get_total_active_balance(state) // SLOTS_PER_EPOCH
// quorum = per_slot_balance * BUILDER_PAYMENT_THRESHOLD_NUMERATOR
// return uint64(quorum // BUILDER_PAYMENT_THRESHOLD_DENOMINATOR)
func builderQuorumThreshold(state state.BeaconState) (primitives.Gwei, error) {
activeBalance, err := helpers.TotalActiveBalance(state)
if err != nil {
return 0, errors.Wrap(err, "could not get total active balance")
}
cfg := params.BeaconConfig()
slotsPerEpoch := uint64(cfg.SlotsPerEpoch)
numerator := cfg.BuilderPaymentThresholdNumerator
denominator := cfg.BuilderPaymentThresholdDenominator
activeBalancePerSlot := activeBalance / slotsPerEpoch
quorum := (activeBalancePerSlot * numerator) / denominator
return primitives.Gwei(quorum), nil
}