Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: change the initial epoch to 1 #205

Merged
merged 4 commits into from
Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
31 changes: 24 additions & 7 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
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)
}
30 changes: 28 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,25 @@ 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 {
// TODO: send remaining outstanding rewards to the fee pool
dongsam marked this conversation as resolved.
Show resolved Hide resolved
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 +377,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