github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/epoch/precompute/reward_penalty.go (about) 1 package precompute 2 3 import ( 4 "github.com/pkg/errors" 5 types "github.com/prysmaticlabs/eth2-types" 6 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 7 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 8 "github.com/prysmaticlabs/prysm/shared/mathutil" 9 "github.com/prysmaticlabs/prysm/shared/params" 10 ) 11 12 type attesterRewardsFunc func(iface.ReadOnlyBeaconState, *Balance, []*Validator) ([]uint64, []uint64, error) 13 type proposerRewardsFunc func(iface.ReadOnlyBeaconState, *Balance, []*Validator) ([]uint64, error) 14 15 // ProcessRewardsAndPenaltiesPrecompute processes the rewards and penalties of individual validator. 16 // This is an optimized version by passing in precomputed validator attesting records and and total epoch balances. 17 func ProcessRewardsAndPenaltiesPrecompute( 18 state iface.BeaconState, 19 pBal *Balance, 20 vp []*Validator, 21 attRewardsFunc attesterRewardsFunc, 22 proRewardsFunc proposerRewardsFunc, 23 ) (iface.BeaconState, error) { 24 // Can't process rewards and penalties in genesis epoch. 25 if helpers.CurrentEpoch(state) == 0 { 26 return state, nil 27 } 28 29 numOfVals := state.NumValidators() 30 // Guard against an out-of-bounds using validator balance precompute. 31 if len(vp) != numOfVals || len(vp) != state.BalancesLength() { 32 return state, errors.New("precomputed registries not the same length as state registries") 33 } 34 35 attsRewards, attsPenalties, err := attRewardsFunc(state, pBal, vp) 36 if err != nil { 37 return nil, errors.Wrap(err, "could not get attester attestation delta") 38 } 39 proposerRewards, err := proRewardsFunc(state, pBal, vp) 40 if err != nil { 41 return nil, errors.Wrap(err, "could not get proposer attestation delta") 42 } 43 validatorBals := state.Balances() 44 for i := 0; i < numOfVals; i++ { 45 vp[i].BeforeEpochTransitionBalance = validatorBals[i] 46 47 // Compute the post balance of the validator after accounting for the 48 // attester and proposer rewards and penalties. 49 validatorBals[i] = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i]) 50 validatorBals[i] = helpers.DecreaseBalanceWithVal(validatorBals[i], attsPenalties[i]) 51 52 vp[i].AfterEpochTransitionBalance = validatorBals[i] 53 } 54 55 if err := state.SetBalances(validatorBals); err != nil { 56 return nil, errors.Wrap(err, "could not set validator balances") 57 } 58 59 return state, nil 60 } 61 62 // AttestationsDelta computes and returns the rewards and penalties differences for individual validators based on the 63 // voting records. 64 func AttestationsDelta(state iface.ReadOnlyBeaconState, pBal *Balance, vp []*Validator) ([]uint64, []uint64, error) { 65 numOfVals := state.NumValidators() 66 rewards := make([]uint64, numOfVals) 67 penalties := make([]uint64, numOfVals) 68 prevEpoch := helpers.PrevEpoch(state) 69 finalizedEpoch := state.FinalizedCheckpointEpoch() 70 71 sqrtActiveCurrentEpoch := mathutil.IntegerSquareRoot(pBal.ActiveCurrentEpoch) 72 for i, v := range vp { 73 rewards[i], penalties[i] = attestationDelta(pBal, sqrtActiveCurrentEpoch, v, prevEpoch, finalizedEpoch) 74 } 75 return rewards, penalties, nil 76 } 77 78 func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator, prevEpoch, finalizedEpoch types.Epoch) (uint64, uint64) { 79 if !EligibleForRewards(v) || pBal.ActiveCurrentEpoch == 0 { 80 return 0, 0 81 } 82 83 baseRewardsPerEpoch := params.BeaconConfig().BaseRewardsPerEpoch 84 effectiveBalanceIncrement := params.BeaconConfig().EffectiveBalanceIncrement 85 vb := v.CurrentEpochEffectiveBalance 86 br := vb * params.BeaconConfig().BaseRewardFactor / sqrtActiveCurrentEpoch / baseRewardsPerEpoch 87 r, p := uint64(0), uint64(0) 88 currentEpochBalance := pBal.ActiveCurrentEpoch / effectiveBalanceIncrement 89 90 // Process source reward / penalty 91 if v.IsPrevEpochAttester && !v.IsSlashed { 92 proposerReward := br / params.BeaconConfig().ProposerRewardQuotient 93 maxAttesterReward := br - proposerReward 94 r += maxAttesterReward / uint64(v.InclusionDistance) 95 96 if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { 97 // Since full base reward will be canceled out by inactivity penalty deltas, 98 // optimal participation receives full base reward compensation here. 99 r += br 100 } else { 101 rewardNumerator := br * (pBal.PrevEpochAttested / effectiveBalanceIncrement) 102 r += rewardNumerator / currentEpochBalance 103 104 } 105 } else { 106 p += br 107 } 108 109 // Process target reward / penalty 110 if v.IsPrevEpochTargetAttester && !v.IsSlashed { 111 if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { 112 // Since full base reward will be canceled out by inactivity penalty deltas, 113 // optimal participation receives full base reward compensation here. 114 r += br 115 } else { 116 rewardNumerator := br * (pBal.PrevEpochTargetAttested / effectiveBalanceIncrement) 117 r += rewardNumerator / currentEpochBalance 118 } 119 } else { 120 p += br 121 } 122 123 // Process head reward / penalty 124 if v.IsPrevEpochHeadAttester && !v.IsSlashed { 125 if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { 126 // Since full base reward will be canceled out by inactivity penalty deltas, 127 // optimal participation receives full base reward compensation here. 128 r += br 129 } else { 130 rewardNumerator := br * (pBal.PrevEpochHeadAttested / effectiveBalanceIncrement) 131 r += rewardNumerator / currentEpochBalance 132 } 133 } else { 134 p += br 135 } 136 137 // Process finality delay penalty 138 if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { 139 // If validator is performing optimally, this cancels all rewards for a neutral balance. 140 proposerReward := br / params.BeaconConfig().ProposerRewardQuotient 141 p += baseRewardsPerEpoch*br - proposerReward 142 // Apply an additional penalty to validators that did not vote on the correct target or has been slashed. 143 // Equivalent to the following condition from the spec: 144 // `index not in get_unslashed_attesting_indices(state, matching_target_attestations)` 145 if !v.IsPrevEpochTargetAttester || v.IsSlashed { 146 finalityDelay := helpers.FinalityDelay(prevEpoch, finalizedEpoch) 147 p += vb * uint64(finalityDelay) / params.BeaconConfig().InactivityPenaltyQuotient 148 } 149 } 150 return r, p 151 } 152 153 // ProposersDelta computes and returns the rewards and penalties differences for individual validators based on the 154 // proposer inclusion records. 155 func ProposersDelta(state iface.ReadOnlyBeaconState, pBal *Balance, vp []*Validator) ([]uint64, error) { 156 numofVals := state.NumValidators() 157 rewards := make([]uint64, numofVals) 158 159 totalBalance := pBal.ActiveCurrentEpoch 160 balanceSqrt := mathutil.IntegerSquareRoot(totalBalance) 161 // Balance square root cannot be 0, this prevents division by 0. 162 if balanceSqrt == 0 { 163 balanceSqrt = 1 164 } 165 166 baseRewardFactor := params.BeaconConfig().BaseRewardFactor 167 baseRewardsPerEpoch := params.BeaconConfig().BaseRewardsPerEpoch 168 proposerRewardQuotient := params.BeaconConfig().ProposerRewardQuotient 169 for _, v := range vp { 170 if uint64(v.ProposerIndex) >= uint64(len(rewards)) { 171 // This should never happen with a valid state / validator. 172 return nil, errors.New("proposer index out of range") 173 } 174 // Only apply inclusion rewards to proposer only if the attested hasn't been slashed. 175 if v.IsPrevEpochAttester && !v.IsSlashed { 176 vBalance := v.CurrentEpochEffectiveBalance 177 baseReward := vBalance * baseRewardFactor / balanceSqrt / baseRewardsPerEpoch 178 proposerReward := baseReward / proposerRewardQuotient 179 rewards[v.ProposerIndex] += proposerReward 180 } 181 } 182 return rewards, nil 183 } 184 185 // EligibleForRewards for validator. 186 // 187 // Spec code: 188 // if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch) 189 func EligibleForRewards(v *Validator) bool { 190 return v.IsActivePrevEpoch || (v.IsSlashed && !v.IsWithdrawableCurrentEpoch) 191 }