Skip to content

Commit 7869f4f

Browse files
authored
Merge pull request #323 from arkeonetwork/fix/Incorrect-reward-distribution-logic-in-ValidatorPayout
fix: incorrect reward distribution logic in validator payout
2 parents bd35978 + 2db639e commit 7869f4f

File tree

4 files changed

+47
-65
lines changed

4 files changed

+47
-65
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Contains all the PRs that improved the code without changing the behaviors.
4343
### Fixed
4444
- added VersionForAddress field to GenesisState
4545
46+
### Fixed
47+
- validator rewards payout
48+
4649
# v1.0.5-Prerelease
4750
### Added
4851
- Arkeo testnet validator addresses to airdrop

x/arkeo/keeper/manager.go

+14-45
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
219219

220220
// sum tokens
221221
total := cosmos.ZeroDec()
222+
var totalRemainder cosmos.Dec = cosmos.ZeroDec()
222223
for _, vote := range votes {
223224
val, err := mgr.sk.ValidatorByConsAddr(ctx, vote.Validator.Address)
224225
if err != nil {
@@ -234,8 +235,6 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
234235
return nil
235236
}
236237

237-
var totalRemainder cosmos.Dec = cosmos.ZeroDec()
238-
239238
for _, vote := range votes {
240239
if vote.BlockIdFlag.String() == "BLOCK_ID_FLAG_ABSENT" || vote.BlockIdFlag.String() == "BLOCK_ID_FLAG_UNKNOWN" {
241240
mgr.keeper.Logger().Info(fmt.Sprintf("validator rewards skipped due to lack of signature: %s, validator : %s ", vote.BlockIdFlag.String(), string(vote.Validator.GetAddress())))
@@ -265,56 +264,26 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
265264
acc := cosmos.AccAddress(val.GetOperator())
266265

267266
totalReward := common.GetSafeShare(val.GetDelegatorShares(), total, blockReward.Amount)
268-
validatorReward := cosmos.ZeroDec()
269-
rateBasisPts := val.GetCommission().MulInt64(100)
270267

271-
delegates, err := mgr.sk.GetValidatorDelegations(ctx, valBz)
272-
if err != nil {
273-
panic(err)
268+
if totalReward.IsZero() {
269+
continue
274270
}
275271

276-
for _, delegate := range delegates {
277-
delegateAcc, err := cosmos.AccAddressFromBech32(delegate.DelegatorAddress)
278-
if err != nil {
279-
mgr.keeper.Logger().Error("unable to fetch delegate address", "delegate", delegate.DelegatorAddress, "error", err)
280-
continue
281-
}
282-
delegateReward := common.GetSafeShare(delegate.GetShares(), val.GetDelegatorShares(), totalReward)
283-
if acc.String() != delegate.DelegatorAddress {
284-
valFee := common.GetSafeShare(rateBasisPts, cosmos.NewDec(configs.MaxBasisPoints), delegateReward)
285-
delegateReward = delegateReward.Sub(valFee)
286-
validatorReward = validatorReward.Add(valFee)
287-
}
272+
intValidatorReward := totalReward.TruncateInt()
273+
remainder := totalReward.Sub(cosmos.NewDec(intValidatorReward.Int64()))
274+
totalRemainder = totalRemainder.Add(remainder)
288275

289-
intReward, remainder := delegateReward.TruncateInt(), delegateReward.Sub(cosmos.NewDec(delegateReward.TruncateInt().Int64()))
290-
totalRemainder = totalRemainder.Add(remainder)
291-
if err := mgr.keeper.SendFromModuleToModule(ctx, types.ReserveName, disttypes.ModuleName, cosmos.NewCoins(cosmos.NewCoin(blockReward.Denom, intReward))); err != nil {
292-
mgr.keeper.Logger().Error("unable to pay rewards to delegate", "delegate", delegate.DelegatorAddress, "error", err)
293-
continue
294-
}
295-
296-
if err := mgr.keeper.AllocateTokensToValidator(ctx, val, sdk.NewDecCoins(sdk.NewDecCoin(blockReward.Denom, intReward))); err != nil {
297-
mgr.keeper.Logger().Error("unable to pay rewards to delegate", "delegate", delegate.DelegatorAddress, "error", err)
298-
continue
299-
}
300-
mgr.keeper.Logger().Info("delegate rewarded", "delegate", delegateAcc.String(), "amount", delegateReward)
276+
if err := mgr.keeper.SendFromModuleToModule(ctx, types.ReserveName, disttypes.ModuleName, cosmos.NewCoins(cosmos.NewCoin(blockReward.Denom, intValidatorReward))); err != nil {
277+
mgr.keeper.Logger().Error("unable to pay rewards to delegate", "delegate", val.GetOperator(), "error", err)
278+
continue
301279
}
302-
303-
if !validatorReward.IsZero() {
304-
intValidatorReward, remainder := validatorReward.TruncateInt(), validatorReward.Sub(cosmos.NewDec(validatorReward.TruncateInt().Int64()))
305-
totalRemainder = totalRemainder.Add(remainder)
306-
if err := mgr.keeper.SendFromModuleToModule(ctx, types.ReserveName, disttypes.ModuleName, cosmos.NewCoins(cosmos.NewCoin(blockReward.Denom, intValidatorReward))); err != nil {
307-
mgr.keeper.Logger().Error("unable to pay rewards to delegate", "delegate", val.GetOperator(), "error", err)
308-
continue
309-
}
310-
if err := mgr.keeper.AllocateTokensToValidator(ctx, val, sdk.NewDecCoins(sdk.NewDecCoin(blockReward.Denom, intValidatorReward))); err != nil {
311-
mgr.keeper.Logger().Error("unable to pay rewards to validator", "validator", val.GetOperator(), "error", err)
312-
continue
313-
}
314-
mgr.keeper.Logger().Info("validator rewards", "validator", acc.String(), "amount", validatorReward)
280+
if err := mgr.keeper.AllocateTokensToValidator(ctx, val, sdk.NewDecCoins(sdk.NewDecCoin(blockReward.Denom, intValidatorReward))); err != nil {
281+
mgr.keeper.Logger().Error("unable to pay rewards to validator", "validator", val.GetOperator(), "error", err)
282+
continue
315283
}
284+
mgr.keeper.Logger().Info("validator rewards", "validator", acc.String(), "amount", intValidatorReward)
316285

317-
if err := mgr.EmitValidatorPayoutEvent(ctx, acc, validatorReward.TruncateInt()); err != nil {
286+
if err := mgr.EmitValidatorPayoutEvent(ctx, acc, totalReward.TruncateInt()); err != nil {
318287
mgr.keeper.Logger().Error("unable to emit validator payout event", "validator", acc.String(), "error", err)
319288
}
320289
}

x/arkeo/keeper/manager_test.go

+24-15
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ func TestValidatorPayouts(t *testing.T) {
326326
totalReserve := sdkmath.NewInt(1000000000).ToLegacyDec()
327327

328328
blockReward := mgr.calcBlockReward(ctx, totalReserve, emissionCurve, blocksPerYear, valCycle)
329-
330329
require.Equal(t, blockReward.Amount.RoundInt64(), int64(2000000))
331330

331+
// Setup validators
332332
pks := simtestutil.CreateTestPubKeys(3)
333333
pk1, err := common.NewPubKeyFromCrypto(pks[0])
334334
require.NoError(t, err)
@@ -345,10 +345,11 @@ func TestValidatorPayouts(t *testing.T) {
345345

346346
valAddrs := simtestutil.ConvertAddrsToValAddrs([]cosmos.AccAddress{acc1, acc2, acc3})
347347

348+
// Create validators with their shares
348349
val1, err := stakingtypes.NewValidator(valAddrs[0].String(), pks[0], stakingtypes.Description{})
349350
require.NoError(t, err)
350351
val1.Tokens = cosmos.NewInt(100)
351-
val1.DelegatorShares = cosmos.NewDec(130) // Validator + Delegations
352+
val1.DelegatorShares = cosmos.NewDec(130)
352353
val1.Status = stakingtypes.Bonded
353354
val1.Commission = stakingtypes.NewCommission(cosmos.NewDecWithPrec(1, 1), cosmos.ZeroDec(), cosmos.ZeroDec())
354355

@@ -373,14 +374,17 @@ func TestValidatorPayouts(t *testing.T) {
373374
require.NoError(t, sk.SetNewValidatorByPowerIndex(ctx, val))
374375
}
375376

377+
// Setup delegations
376378
delAcc1 := types.GetRandomBech32Addr()
377379
delAcc2 := types.GetRandomBech32Addr()
378380
delAcc3 := types.GetRandomBech32Addr()
379381

382+
// Set validator self-delegations
380383
require.NoError(t, sk.SetDelegation(ctx, stakingtypes.NewDelegation(acc1.String(), valAddrs[0].String(), cosmos.NewDec(100))))
381384
require.NoError(t, sk.SetDelegation(ctx, stakingtypes.NewDelegation(acc2.String(), valAddrs[1].String(), cosmos.NewDec(200))))
382385
require.NoError(t, sk.SetDelegation(ctx, stakingtypes.NewDelegation(acc3.String(), valAddrs[2].String(), cosmos.NewDec(500))))
383386

387+
// Set other delegations
384388
del1 := stakingtypes.NewDelegation(delAcc1.String(), valAddrs[0].String(), cosmos.NewDec(10))
385389
del2 := stakingtypes.NewDelegation(delAcc2.String(), valAddrs[1].String(), cosmos.NewDec(20))
386390
del3 := stakingtypes.NewDelegation(delAcc3.String(), valAddrs[2].String(), cosmos.NewDec(20))
@@ -407,36 +411,41 @@ func TestValidatorPayouts(t *testing.T) {
407411
}
408412
}
409413

414+
// Check initial module balance
410415
moduleBalance := k.GetBalanceOfModule(ctx, types.ReserveName, configs.Denom)
411416
require.Equal(t, moduleBalance.Int64(), int64(20000000000000))
412417

418+
// Get reserve supply and execute validator payout
413419
reserveSupply, err := mgr.reserveSupply(ctx)
414420
require.NoError(t, err)
415-
416421
require.NoError(t, mgr.ValidatorPayout(ctx, votes, reserveSupply))
417422

418-
totalBal := cosmos.ZeroInt()
423+
// Calculate expected total shares and rewards
424+
totalShares := val1.DelegatorShares.Add(val2.DelegatorShares).Add(val3.DelegatorShares)
419425

426+
// Check rewards for each validator
427+
expectedVal1Reward := common.GetSafeShare(val1.DelegatorShares, totalShares, reserveSupply.Amount)
428+
expectedVal2Reward := common.GetSafeShare(val2.DelegatorShares, totalShares, reserveSupply.Amount)
429+
expectedVal3Reward := common.GetSafeShare(val3.DelegatorShares, totalShares, reserveSupply.Amount)
430+
431+
// Verify rewards
420432
rewardsAcc1, err := k.GetValidatorRewards(ctx, acc1.Bytes())
421433
require.NoError(t, err)
422-
require.Equal(t, rewardsAcc1.Rewards.AmountOf(configs.Denom).RoundInt(), sdkmath.NewInt(2588235294117))
423-
totalBal = totalBal.Add(rewardsAcc1.Rewards.AmountOf(configs.Denom).RoundInt())
434+
require.Equal(t, rewardsAcc1.Rewards.AmountOf(configs.Denom).TruncateInt(), expectedVal1Reward.TruncateInt())
424435

425436
rewardsAcc2, err := k.GetValidatorRewards(ctx, acc2.Bytes())
426437
require.NoError(t, err)
427-
require.Equal(t, rewardsAcc2.Rewards.AmountOf(configs.Denom).RoundInt(), sdkmath.NewInt(5176470588234))
428-
totalBal = totalBal.Add(rewardsAcc2.Rewards.AmountOf(configs.Denom).RoundInt())
438+
require.Equal(t, rewardsAcc2.Rewards.AmountOf(configs.Denom).TruncateInt(), expectedVal2Reward.TruncateInt())
429439

430440
rewardsAcc3, err := k.GetValidatorRewards(ctx, acc3.Bytes())
431441
require.NoError(t, err)
432-
require.Equal(t, rewardsAcc3.Rewards.AmountOf(configs.Denom).RoundInt(), sdkmath.NewInt(12235294117646))
433-
totalBal = totalBal.Add(rewardsAcc3.Rewards.AmountOf(configs.Denom).RoundInt())
434-
435-
require.Equal(t, totalBal.ToLegacyDec(), sdkmath.LegacyNewDec(19999999999997))
442+
require.Equal(t, rewardsAcc3.Rewards.AmountOf(configs.Denom).TruncateInt(), expectedVal3Reward.TruncateInt())
436443

444+
// Verify module balances
437445
moduleBalance = k.GetBalanceOfModule(ctx, types.ReserveName, configs.Denom)
438-
require.Equal(t, moduleBalance.ToLegacyDec().RoundInt64(), int64(0))
439-
p, err := k.GetCommunityPool(ctx)
446+
require.Equal(t, moduleBalance.Int64(), int64(0))
447+
// Check community pool for remainder
448+
pool, err := k.GetCommunityPool(ctx)
440449
require.NoError(t, err)
441-
require.Equal(t, p.CommunityPool.AmountOf(configs.Denom), sdkmath.LegacyNewDec(10003))
450+
require.True(t, pool.CommunityPool.AmountOf(configs.Denom).GT(cosmos.ZeroDec()))
442451
}

x/arkeo/module.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ import (
2727
)
2828

2929
var (
30-
_ module.AppModule = AppModule{}
31-
_ module.AppModuleBasic = AppModuleBasic{}
32-
_ module.HasABCIEndBlock = AppModule{}
33-
_ module.HasGenesis = AppModule{}
34-
_ appmodule.AppModule = AppModule{}
30+
_ module.AppModule = AppModule{}
31+
_ module.AppModuleBasic = AppModuleBasic{}
32+
_ module.HasABCIEndBlock = AppModule{}
33+
_ module.HasGenesis = AppModule{}
34+
_ appmodule.AppModule = AppModule{}
35+
_ appmodule.HasBeginBlocker = AppModule{}
3536
)
3637

3738
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)