Files
prysm/beacon-chain/core/epoch/precompute/reward_penalty.go
Preston Van Loon 2fd6bd8150 Add golang.org/x/tools modernize static analyzer and fix violations (#15946)
* Ran gopls modernize to fix everything

go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

* Override rules_go provided dependency for golang.org/x/tools to v0.38.0.

To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches.

* Fix buildtag violations and ignore buildtag violations in external

* Introduce modernize analyzer package.

* Add modernize "any" analyzer.

* Fix violations of any analyzer

* Add modernize "appendclipped" analyzer.

* Fix violations of appendclipped

* Add modernize "bloop" analyzer.

* Add modernize "fmtappendf" analyzer.

* Add modernize "forvar" analyzer.

* Add modernize "mapsloop" analyzer.

* Add modernize "minmax" analyzer.

* Fix violations of minmax analyzer

* Add modernize "omitzero" analyzer.

* Add modernize "rangeint" analyzer.

* Fix violations of rangeint.

* Add modernize "reflecttypefor" analyzer.

* Fix violations of reflecttypefor analyzer.

* Add modernize "slicescontains" analyzer.

* Add modernize "slicessort" analyzer.

* Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686.

* Add modernize "stringscutprefix" analyzer.

* Add modernize "stringsbuilder" analyzer.

* Fix violations of stringsbuilder analyzer.

* Add modernize "stringsseq" analyzer.

* Add modernize "testingcontext" analyzer.

* Add modernize "waitgroup" analyzer.

* Changelog fragment

* gofmt

* gazelle

* Add modernize "newexpr" analyzer.

* Disable newexpr until go1.26

* Add more details in WORKSPACE on how to update the override

* @nalepae feedback on min()

* gofmt

* Fix violations of forvar
2025-11-14 01:27:22 +00:00

195 lines
7.5 KiB
Go

package precompute
import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
"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/OffchainLabs/prysm/v7/math"
"github.com/pkg/errors"
)
type attesterRewardsFunc func(state.ReadOnlyBeaconState, *Balance, []*Validator) ([]uint64, []uint64, error)
type proposerRewardsFunc func(state.ReadOnlyBeaconState, *Balance, []*Validator) ([]uint64, error)
// ProcessRewardsAndPenaltiesPrecompute processes the rewards and penalties of individual validator.
// This is an optimized version by passing in precomputed validator attesting records and total epoch balances.
func ProcessRewardsAndPenaltiesPrecompute(
state state.BeaconState,
pBal *Balance,
vp []*Validator,
attRewardsFunc attesterRewardsFunc,
proRewardsFunc proposerRewardsFunc,
) (state.BeaconState, error) {
// Can't process rewards and penalties in genesis epoch.
if time.CurrentEpoch(state) == 0 {
return state, nil
}
numOfVals := state.NumValidators()
// Guard against an out-of-bounds using validator balance precompute.
if len(vp) != numOfVals || len(vp) != state.BalancesLength() {
return state, errors.New("precomputed registries not the same length as state registries")
}
attsRewards, attsPenalties, err := attRewardsFunc(state, pBal, vp)
if err != nil {
return nil, errors.Wrap(err, "could not get attester attestation delta")
}
proposerRewards, err := proRewardsFunc(state, pBal, vp)
if err != nil {
return nil, errors.Wrap(err, "could not get proposer attestation delta")
}
validatorBals := state.Balances()
for i := range numOfVals {
vp[i].BeforeEpochTransitionBalance = validatorBals[i]
// Compute the post balance of the validator after accounting for the
// attester and proposer rewards and penalties.
validatorBals[i], err = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i])
if err != nil {
return nil, err
}
validatorBals[i] = helpers.DecreaseBalanceWithVal(validatorBals[i], attsPenalties[i])
vp[i].AfterEpochTransitionBalance = validatorBals[i]
}
if err := state.SetBalances(validatorBals); err != nil {
return nil, errors.Wrap(err, "could not set validator balances")
}
return state, nil
}
// AttestationsDelta computes and returns the rewards and penalties differences for individual validators based on the
// voting records.
func AttestationsDelta(state state.ReadOnlyBeaconState, pBal *Balance, vp []*Validator) ([]uint64, []uint64, error) {
numOfVals := state.NumValidators()
rewards := make([]uint64, numOfVals)
penalties := make([]uint64, numOfVals)
prevEpoch := time.PrevEpoch(state)
finalizedEpoch := state.FinalizedCheckpointEpoch()
sqrtActiveCurrentEpoch := math.CachedSquareRoot(pBal.ActiveCurrentEpoch)
for i, v := range vp {
rewards[i], penalties[i] = attestationDelta(pBal, sqrtActiveCurrentEpoch, v, prevEpoch, finalizedEpoch)
}
return rewards, penalties, nil
}
func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator, prevEpoch, finalizedEpoch primitives.Epoch) (uint64, uint64) {
if !EligibleForRewards(v) || pBal.ActiveCurrentEpoch == 0 {
return 0, 0
}
baseRewardsPerEpoch := params.BeaconConfig().BaseRewardsPerEpoch
effectiveBalanceIncrement := params.BeaconConfig().EffectiveBalanceIncrement
vb := v.CurrentEpochEffectiveBalance
br := vb * params.BeaconConfig().BaseRewardFactor / sqrtActiveCurrentEpoch / baseRewardsPerEpoch
r, p := uint64(0), uint64(0)
currentEpochBalance := pBal.ActiveCurrentEpoch / effectiveBalanceIncrement
// Process source reward / penalty
if v.IsPrevEpochAttester && !v.IsSlashed {
proposerReward := br / params.BeaconConfig().ProposerRewardQuotient
maxAttesterReward := br - proposerReward
r += maxAttesterReward / uint64(v.InclusionDistance)
if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
// Since full base reward will be canceled out by inactivity penalty deltas,
// optimal participation receives full base reward compensation here.
r += br
} else {
rewardNumerator := br * (pBal.PrevEpochAttested / effectiveBalanceIncrement)
r += rewardNumerator / currentEpochBalance
}
} else {
p += br
}
// Process target reward / penalty
if v.IsPrevEpochTargetAttester && !v.IsSlashed {
if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
// Since full base reward will be canceled out by inactivity penalty deltas,
// optimal participation receives full base reward compensation here.
r += br
} else {
rewardNumerator := br * (pBal.PrevEpochTargetAttested / effectiveBalanceIncrement)
r += rewardNumerator / currentEpochBalance
}
} else {
p += br
}
// Process head reward / penalty
if v.IsPrevEpochHeadAttester && !v.IsSlashed {
if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
// Since full base reward will be canceled out by inactivity penalty deltas,
// optimal participation receives full base reward compensation here.
r += br
} else {
rewardNumerator := br * (pBal.PrevEpochHeadAttested / effectiveBalanceIncrement)
r += rewardNumerator / currentEpochBalance
}
} else {
p += br
}
// Process finality delay penalty
if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
// If validator is performing optimally, this cancels all rewards for a neutral balance.
proposerReward := br / params.BeaconConfig().ProposerRewardQuotient
p += baseRewardsPerEpoch*br - proposerReward
// Apply an additional penalty to validators that did not vote on the correct target or has been slashed.
// Equivalent to the following condition from the spec:
// `index not in get_unslashed_attesting_indices(state, matching_target_attestations)`
if !v.IsPrevEpochTargetAttester || v.IsSlashed {
finalityDelay := helpers.FinalityDelay(prevEpoch, finalizedEpoch)
p += vb * uint64(finalityDelay) / params.BeaconConfig().InactivityPenaltyQuotient
}
}
return r, p
}
// ProposersDelta computes and returns the rewards and penalties differences for individual validators based on the
// proposer inclusion records.
func ProposersDelta(state state.ReadOnlyBeaconState, pBal *Balance, vp []*Validator) ([]uint64, error) {
numofVals := state.NumValidators()
rewards := make([]uint64, numofVals)
totalBalance := pBal.ActiveCurrentEpoch
balanceSqrt := math.CachedSquareRoot(totalBalance)
// Balance square root cannot be 0, this prevents division by 0.
if balanceSqrt == 0 {
balanceSqrt = 1
}
baseRewardFactor := params.BeaconConfig().BaseRewardFactor
baseRewardsPerEpoch := params.BeaconConfig().BaseRewardsPerEpoch
proposerRewardQuotient := params.BeaconConfig().ProposerRewardQuotient
for _, v := range vp {
if uint64(v.ProposerIndex) >= uint64(len(rewards)) {
// This should never happen with a valid state / validator.
return nil, errors.New("proposer index out of range")
}
// Only apply inclusion rewards to proposer only if the attested hasn't been slashed.
if v.IsPrevEpochAttester && !v.IsSlashed {
vBalance := v.CurrentEpochEffectiveBalance
baseReward := vBalance * baseRewardFactor / balanceSqrt / baseRewardsPerEpoch
proposerReward := baseReward / proposerRewardQuotient
rewards[v.ProposerIndex] += proposerReward
}
}
return rewards, nil
}
// EligibleForRewards for validator.
//
// Spec code:
// if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
func EligibleForRewards(v *Validator) bool {
return v.IsActivePrevEpoch || (v.IsSlashed && !v.IsWithdrawableCurrentEpoch)
}