A question about reward calculation

I was making some changes in the code and I saw this method which is for reward calculation:

// WithUpdatedRewards returns an updated number of algos in an AccountData
// to reflect rewards up to some rewards level.
func (u AccountData) WithUpdatedRewards(proto config.ConsensusParams, rewardsLevel uint64) AccountData {
	if u.Status != NotParticipating {
		var ot OverflowTracker
		rewardsUnits := u.MicroAlgos.RewardUnits(proto)
		rewardsDelta := ot.Sub(rewardsLevel, u.RewardsBase)
		rewards := MicroAlgos{Raw: ot.Mul(rewardsUnits, rewardsDelta)}
		u.MicroAlgos = ot.AddA(u.MicroAlgos, rewards)
		if ot.Overflowed {
			logging.Base().Panicf("AccountData.WithUpdatedRewards(): overflowed account balance when applying rewards %v + %d*(%d-%d)", u.MicroAlgos, rewardsUnits, rewardsLevel, u.RewardsBase)
		}
		u.RewardsBase = rewardsLevel
		// The total reward over the lifetime of the account could exceed a 64-bit value. As a result
		// this rewardAlgos counter could potentially roll over.
		u.RewardedMicroAlgos = MicroAlgos{Raw: (u.RewardedMicroAlgos.Raw + rewards.Raw)}
	}

	return u
}

With this code a nonparticipating account can get all his deferred rewards if at some time he changes his status to “participating”. Is this intentional? Shouldn’t the code be something like this:


// WithUpdatedRewards returns an updated number of algos in an AccountData
// to reflect rewards up to some rewards level.
func (u AccountData) WithUpdatedRewards(proto config.ConsensusParams, rewardsLevel uint64) AccountData {
	if u.Status != NotParticipating {
		var ot OverflowTracker
		rewardsUnits := u.MicroAlgos.RewardUnits(proto)
		rewardsDelta := ot.Sub(rewardsLevel, u.RewardsBase)
		rewards := MicroAlgos{Raw: ot.Mul(rewardsUnits, rewardsDelta)}
		u.MicroAlgos = ot.AddA(u.MicroAlgos, rewards)
		if ot.Overflowed {
			logging.Base().Panicf("AccountData.WithUpdatedRewards(): overflowed account balance when applying rewards %v + %d*(%d-%d)", u.MicroAlgos, rewardsUnits, rewardsLevel, u.RewardsBase)
		}
		// The total reward over the lifetime of the account could exceed a 64-bit value. As a result
		// this rewardAlgos counter could potentially roll over.
		u.RewardedMicroAlgos = MicroAlgos{Raw: (u.RewardedMicroAlgos.Raw + rewards.Raw)}
	}
	// we should update the RewardsBase for non-participants to make sure they can't get rewards later
	u.RewardsBase = rewardsLevel
	return u
}

And if non-participants are eligible to receive rewards, why shouldn’t we omit the if clause?

@aybehrouz,

non-participating accounts are excluded from receiving rewards. When calculating the rewards, the total rewards unit excludes the non-participating accounts:

// RewardUnits returns the sum of reward units held under ``participating''
// account status values (Online and Offline).  It excludes units held
// by NotParticipating accounts.
func (at *AccountTotals) RewardUnits() uint64 {
	res, overflowed := basics.OAdd(at.Online.RewardUnits, at.Offline.RewardUnits)
	if overflowed {
		logging.Base().Panicf("AccountTotals.RewardUnits(): overflow %v + %v", at.Online, at.Offline)
	}
	return res
}

Also, changing the account status from non-participating is not supported:

// Keyreg applies a KeyRegistration transaction using the Balances interface.
func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, balances Balances, spec transactions.SpecialAddresses, ad *transactions.ApplyData) error {

    ...

	// non-participatory accounts cannot be brought online (or offline)
	if record.Status == basics.NotParticipating {
		return fmt.Errorf("cannot change online/offline status of non-participating account %v", header.Sender)
	}

    ...
1 Like