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  }