Skip to content

Commit

Permalink
fix: change the initial epoch to 1
Browse files Browse the repository at this point in the history
Merge pull request #205 from hallazzang/192-initial-epoch
  • Loading branch information
hallazzang authored Nov 4, 2021
2 parents debb2e6 + 226a55d commit ec4d5f5
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 23 deletions.
18 changes: 13 additions & 5 deletions x/farming/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keeper_test

import (
"fmt"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -287,12 +289,18 @@ func (suite *KeeperTestSuite) TestExportGenesis() {
{
"HistoricalRewards",
func() {
suite.Require().Len(genState.HistoricalRewardsRecords, 2)
suite.Require().Len(genState.HistoricalRewardsRecords, 4)
for _, record := range genState.HistoricalRewardsRecords {
suite.Require().Equal(uint64(0), record.Epoch)
suite.Require().Contains([]string{denom1, denom2}, record.StakingCoinDenom)
suite.Require().False(record.HistoricalRewards.CumulativeUnitRewards.IsZero())
// TODO: need to check actual value?
switch record.Epoch {
case 0:
suite.Require().True(record.HistoricalRewards.CumulativeUnitRewards.IsZero())
case 1:
// TODO: need to check actual value?
suite.Require().False(record.HistoricalRewards.CumulativeUnitRewards.IsZero())
default:
panic(fmt.Sprintf("unexpected epoch %d", record.Epoch))
}
}
},
},
Expand All @@ -319,7 +327,7 @@ func (suite *KeeperTestSuite) TestExportGenesis() {
func() {
suite.Require().Len(genState.CurrentEpochRecords, 2)
for _, record := range genState.CurrentEpochRecords {
suite.Require().Equal(uint64(1), record.CurrentEpoch)
suite.Require().Equal(uint64(2), record.CurrentEpoch)
}
},
},
Expand Down
3 changes: 1 addition & 2 deletions x/farming/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,7 @@ func (suite *KeeperTestSuite) TestGRPCRewards() {
nil,
},
} {
cacheCtx, _ := suite.ctx.CacheContext() // TODO: can we omit the 'cached' context?
resp, err := suite.querier.Rewards(sdk.WrapSDKContext(cacheCtx), tc.req)
resp, err := suite.querier.Rewards(sdk.WrapSDKContext(suite.ctx), tc.req)
if tc.expectErr {
suite.Require().Error(err)
} else {
Expand Down
6 changes: 3 additions & 3 deletions x/farming/keeper/invariants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,22 @@ func (suite *KeeperTestSuite) TestRemainingRewardsAmountInvariant() {

// Withdrawable rewards amount in the store > balance of rewards reserve acc.
// Should not be OK.
k.SetHistoricalRewards(ctx, denom1, 1, types.HistoricalRewards{
k.SetHistoricalRewards(ctx, denom1, 2, types.HistoricalRewards{
CumulativeUnitRewards: sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, 3)),
})
_, broken = farmingkeeper.RemainingRewardsAmountInvariant(k)(ctx)
suite.Require().True(broken)

// Withdrawable rewards amount in the store <= balance of rewards reserve acc.
// Should be OK.
k.SetHistoricalRewards(ctx, denom1, 1, types.HistoricalRewards{
k.SetHistoricalRewards(ctx, denom1, 2, types.HistoricalRewards{
CumulativeUnitRewards: sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, 1)),
})
_, broken = farmingkeeper.RemainingRewardsAmountInvariant(k)(ctx)
suite.Require().False(broken)

// Reset.
k.SetHistoricalRewards(ctx, denom1, 1, types.HistoricalRewards{
k.SetHistoricalRewards(ctx, denom1, 2, types.HistoricalRewards{
CumulativeUnitRewards: sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, 2)),
})
_, broken = farmingkeeper.RemainingRewardsAmountInvariant(k)(ctx)
Expand Down
32 changes: 24 additions & 8 deletions x/farming/keeper/reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ func (k Keeper) DeleteHistoricalRewards(ctx sdk.Context, stakingCoinDenom string
store.Delete(types.GetHistoricalRewardsKey(stakingCoinDenom, epoch))
}

// DeleteAllHistoricalRewards deletes all historical rewards for a
// staking coin denom.
func (k Keeper) DeleteAllHistoricalRewards(ctx sdk.Context, stakingCoinDenom string) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, types.GetHistoricalRewardsPrefix(stakingCoinDenom))
defer iter.Close()
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
}

// IterateHistoricalRewards iterates through all historical rewards
// stored in the store and invokes callback function for each item.
// Stops the iteration when the callback function returns true.
Expand Down Expand Up @@ -74,6 +85,13 @@ func (k Keeper) SetCurrentEpoch(ctx sdk.Context, stakingCoinDenom string, curren
store.Set(types.GetCurrentEpochKey(stakingCoinDenom), bz)
}

// DeleteCurrentEpoch deletes current epoch info for a given
// staking coin denom.
func (k Keeper) DeleteCurrentEpoch(ctx sdk.Context, stakingCoinDenom string) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetCurrentEpochKey(stakingCoinDenom))
}

// IterateCurrentEpochs iterates through all current epoch infos
// stored in the store and invokes callback function for each item.
// Stops the iteration when the callback function returns true.
Expand Down Expand Up @@ -139,7 +157,10 @@ func (k Keeper) IterateOutstandingRewards(ctx sdk.Context, cb func(stakingCoinDe
// IncreaseOutstandingRewards increases outstanding rewards for a given
// staking coin denom by given amount.
func (k Keeper) IncreaseOutstandingRewards(ctx sdk.Context, stakingCoinDenom string, amount sdk.DecCoins) {
outstanding, _ := k.GetOutstandingRewards(ctx, stakingCoinDenom)
outstanding, found := k.GetOutstandingRewards(ctx, stakingCoinDenom)
if !found {
panic("outstanding rewards not found")
}
outstanding.Rewards = outstanding.Rewards.Add(amount...)
k.SetOutstandingRewards(ctx, stakingCoinDenom, outstanding)
}
Expand All @@ -153,12 +174,8 @@ func (k Keeper) DecreaseOutstandingRewards(ctx sdk.Context, stakingCoinDenom str
if !found {
panic("outstanding rewards not found")
}
if outstanding.Rewards.IsEqual(amount) {
k.DeleteOutstandingRewards(ctx, stakingCoinDenom)
} else {
outstanding.Rewards = outstanding.Rewards.Sub(amount)
k.SetOutstandingRewards(ctx, stakingCoinDenom, outstanding)
}
outstanding.Rewards = outstanding.Rewards.Sub(amount)
k.SetOutstandingRewards(ctx, stakingCoinDenom, outstanding)
}

// CalculateRewards returns rewards accumulated until endingEpoch
Expand Down Expand Up @@ -209,7 +226,6 @@ func (k Keeper) WithdrawRewards(ctx sdk.Context, farmerAcc sdk.AccAddress, staki
}

currentEpoch := k.GetCurrentEpoch(ctx, stakingCoinDenom)
// TODO: handle if currentEpoch is 0
rewards := k.CalculateRewards(ctx, farmerAcc, stakingCoinDenom, currentEpoch-1)
truncatedRewards, _ := rewards.TruncateDecimal()

Expand Down
63 changes: 60 additions & 3 deletions x/farming/keeper/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,69 @@ func (suite *KeeperTestSuite) TestHistoricalRewards() {
count++
return false
})
suite.Require().Equal(count, 3)
suite.Require().Equal(4, count)

// Next, check if cumulative unit rewards is correct.
for i := uint64(0); i < 3; i++ {
for i := uint64(1); i <= 3; i++ {
historical, found := suite.keeper.GetHistoricalRewards(suite.ctx, denom1, i)
suite.Require().True(found)
suite.Require().True(decCoinsEq(sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, int64((i+1)*2))), historical.CumulativeUnitRewards))
suite.Require().True(decCoinsEq(sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, int64(i*2))), historical.CumulativeUnitRewards))
}
}

// Test if initialization and pruning of staking coin info work properly.
func (suite *KeeperTestSuite) TestInitializeAndPruneStakingCoinInfo() {
suite.SetFixedAmountPlan(1, suite.addrs[4], map[string]string{denom1: "1"}, map[string]int64{denom3: 1000000})

suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000)))

suite.Require().Equal(uint64(0), suite.keeper.GetCurrentEpoch(suite.ctx, denom1))
_, found := suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 0)
suite.Require().False(found)
_, found = suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 1)
suite.Require().False(found)
_, found = suite.keeper.GetOutstandingRewards(suite.ctx, denom1)
suite.Require().False(found)

suite.AdvanceEpoch()

suite.Require().Equal(uint64(1), suite.keeper.GetCurrentEpoch(suite.ctx, denom1))
historical, found := suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 0)
suite.Require().True(found)
suite.Require().True(decCoinsEq(sdk.DecCoins{}, historical.CumulativeUnitRewards))
outstanding, found := suite.keeper.GetOutstandingRewards(suite.ctx, denom1)
suite.Require().True(found)
suite.Require().True(decCoinsEq(sdk.DecCoins{}, outstanding.Rewards))

suite.AdvanceEpoch()

suite.Require().Equal(uint64(2), suite.keeper.GetCurrentEpoch(suite.ctx, denom1))
historical, found = suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 1)
suite.Require().True(found)
suite.Require().True(decCoinsEq(sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, 1)), historical.CumulativeUnitRewards))
outstanding, found = suite.keeper.GetOutstandingRewards(suite.ctx, denom1)
suite.Require().True(found)
suite.Require().True(decCoinsEq(sdk.NewDecCoins(sdk.NewInt64DecCoin(denom3, 1000000)), outstanding.Rewards))
// Historical rewards for epoch 2 must not be present at this point,
// since current epoch is 2, and it has not ended yet.
_, found = suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 2)
suite.Require().False(found)

// Unstake most of the coins. This should not delete any info
// about the staking coin yet.
suite.Unstake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 999999)))
suite.Require().Equal(uint64(2), suite.keeper.GetCurrentEpoch(suite.ctx, denom1))
_, found = suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 1)
suite.Require().True(found)
_, found = suite.keeper.GetOutstandingRewards(suite.ctx, denom1)
suite.Require().True(found)

// Now unstake the rest of the coins. This should delete info
// about the staking coin.
suite.Unstake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1)))
suite.Require().Equal(uint64(0), suite.keeper.GetCurrentEpoch(suite.ctx, denom1))
_, found = suite.keeper.GetHistoricalRewards(suite.ctx, denom1, 1)
suite.Require().False(found)
_, found = suite.keeper.GetOutstandingRewards(suite.ctx, denom1)
suite.Require().False(found)
}
42 changes: 40 additions & 2 deletions x/farming/keeper/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ func (k Keeper) IncreaseTotalStakings(ctx sdk.Context, stakingCoinDenom string,
}
totalStaking.Amount = totalStaking.Amount.Add(amount)
k.SetTotalStakings(ctx, stakingCoinDenom, totalStaking)
if totalStaking.Amount.Equal(amount) {
if err := k.afterStakingCoinAdded(ctx, stakingCoinDenom); err != nil {
panic(err)
}
}
}

// DecreaseTotalStakings decreases total stakings for given staking coin denom
Expand All @@ -196,6 +201,9 @@ func (k Keeper) DecreaseTotalStakings(ctx sdk.Context, stakingCoinDenom string,
}
if totalStaking.Amount.Equal(amount) {
k.DeleteTotalStakings(ctx, stakingCoinDenom)
if err := k.afterStakingCoinRemoved(ctx, stakingCoinDenom); err != nil {
panic(err)
}
} else {
totalStaking.Amount = totalStaking.Amount.Sub(amount)
k.SetTotalStakings(ctx, stakingCoinDenom, totalStaking)
Expand Down Expand Up @@ -235,6 +243,37 @@ func (k Keeper) ReleaseStakingCoins(ctx sdk.Context, farmerAcc sdk.AccAddress, u
return nil
}

// afterStakingCoinAdded is called after a new staking coin denom appeared
// during ProcessQueuedCoins.
func (k Keeper) afterStakingCoinAdded(ctx sdk.Context, stakingCoinDenom string) error {
k.SetHistoricalRewards(ctx, stakingCoinDenom, 0, types.HistoricalRewards{CumulativeUnitRewards: sdk.DecCoins{}})
k.SetCurrentEpoch(ctx, stakingCoinDenom, 1)
k.SetOutstandingRewards(ctx, stakingCoinDenom, types.OutstandingRewards{Rewards: sdk.DecCoins{}})
return nil
}

// afterStakingCoinRemoved is called after a staking coin denom got removed
// during Unstake.
func (k Keeper) afterStakingCoinRemoved(ctx sdk.Context, stakingCoinDenom string) error {
// Send remaining outstanding rewards to the farming fee collector.
// A staking coin is removed only after there is no farmers
// have rewards.
// Note that there should never be any remaining integral rewards
// in general situations, so this exists for confidence.
outstanding, _ := k.GetOutstandingRewards(ctx, stakingCoinDenom)
coins, _ := outstanding.Rewards.TruncateDecimal() // Ignore remainder, since it cannot be sent.
if !coins.IsZero() {
if err := k.bankKeeper.SendCoins(ctx, k.GetRewardsReservePoolAcc(ctx), k.GetFarmingFeeCollectorAcc(ctx), coins); err != nil {
return err
}
}

k.DeleteOutstandingRewards(ctx, stakingCoinDenom)
k.DeleteAllHistoricalRewards(ctx, stakingCoinDenom)
k.DeleteCurrentEpoch(ctx, stakingCoinDenom)
return nil
}

// Stake stores staking coins to queued coins, and it will be processed in the next epoch.
func (k Keeper) Stake(ctx sdk.Context, farmerAcc sdk.AccAddress, amount sdk.Coins) error {
if err := k.ReserveStakingCoins(ctx, farmerAcc, amount); err != nil {
Expand Down Expand Up @@ -350,13 +389,12 @@ func (k Keeper) ProcessQueuedCoins(ctx sdk.Context) {
}

k.DeleteQueuedStaking(ctx, stakingCoinDenom, farmerAcc)
k.IncreaseTotalStakings(ctx, stakingCoinDenom, queuedStaking.Amount)
k.SetStaking(ctx, stakingCoinDenom, farmerAcc, types.Staking{
Amount: staking.Amount.Add(queuedStaking.Amount),
StartingEpoch: k.GetCurrentEpoch(ctx, stakingCoinDenom),
})

k.IncreaseTotalStakings(ctx, stakingCoinDenom, queuedStaking.Amount)

return false
})
}
Expand Down
6 changes: 6 additions & 0 deletions x/farming/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ func GetHistoricalRewardsKey(stakingCoinDenom string, epoch uint64) []byte {
return append(append(HistoricalRewardsKeyPrefix, LengthPrefixString(stakingCoinDenom)...), sdk.Uint64ToBigEndian(epoch)...)
}

// GetHistoricalRewardsPrefix returns a key prefix used to iterate
// historical rewards by a staking coin denom.
func GetHistoricalRewardsPrefix(stakingCoinDenom string) []byte {
return append(HistoricalRewardsKeyPrefix, LengthPrefixString(stakingCoinDenom)...)
}

// GetCurrentEpochKey returns a key for a current epoch info.
func GetCurrentEpochKey(stakingCoinDenom string) []byte {
return append(CurrentEpochKeyPrefix, []byte(stakingCoinDenom)...)
Expand Down

0 comments on commit ec4d5f5

Please sign in to comment.