0g-chain/x/incentive/keeper/rewards_delegator_sync_test.go
Ruaridh cf16029e77
Incentive refactor: hard rewards (#929)
* organise testing committee gen state

* remove repeated test app initialization

* minor fixes from linter in tests

* move more setup to SetupApp

* split up KeeperTestSuite for each reward type

* simplify KeeperTestSuite

* simplify PayoutKeeperSuite

* simplify DelegatorRewardSuite

* simplify SupplyRewardsSuite

* simplify BorrowRewardsSuite

* simplify USDXRewardsSuite

* add auth genesis builder for easier test setup

* migrate all incentive tests to auth builder

* add incentive genesis builder for easier setup
migrate hard incentive tests

* migrate all tests to incentive builder

* add hard genesis builder

* small tidy ups

* deduplicate initialTime from borrow tests

* deduplicate initialtTime from supply tests

* deduplicate initialTime from usdx and keeper tests

* deduplicate initialTime in delgator tests

* deduplicate genesis time in payout test

* deduplicate test app initialization

* make authGenesisBuilder available for all modules

* remove unused pricefeed setup

* export incentive genesis builder

* remove commented out test cases

* migrate cdp test to new test state builders

* migrate vv payout tests to use new builders

* add SynchronizeHardBorrowReward unit test

* extract calculatReward method

* tidy up unit test for borrow rewards

* add helper method to RewardIndexes

* user helper to extract logic from SyncBorrowReward

* add Get methods to (Multi)RewardIndexes

* replace params.Subspace in keeper to test easier

* add unit tests for usdx minting

* refactor InitializeUSDXMintingClaim

* add unit tests for InitializeHardBorrowRewards

* refactor SynchronizeUSDXMintingReward

* add unit tests for UpdateHardBorrowIndexDenoms

* change rewardSource type to Dec
needed by delegation rewards

* fix typo in test names

* refactor UpdateHardBorrowIndexDenoms

* update genesis test TODO to use auth builder

* add skipped test for bug in usdx sync

* extract common method for calculating rewards

* doc comment tidy

* add unit tests for delegator rewards

* tidy up test files

* remove old TODOs

* reaarrange InitializeHardDelegatorReward
to fit with other init reward functions

* duplicate borrow unit tests to create supply tests

* add tests for syncing with zero rewards per second

* refactor SynchronizeHardDelegatorRewards

* refactor supply rewards in same way as borrow

* fix total delegation calculation bug

* fix new usdx reward bug

* fix new supply/borrow reward bug

* remove working comment

* standardize behaviour when global factors missing

* improve documentation for CalculateRewards

* standardize variable names

* remove panic from calculateSingleReward

* wip

* Tidy up comments

* remove wip comment
2021-06-21 22:05:17 +01:00

357 lines
11 KiB
Go

package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/suite"
"github.com/kava-labs/kava/x/incentive/types"
)
// SynchronizeHardDelegatorRewardTests runs unit tests for the keeper.SynchronizeHardDelegatorReward method
//
// inputs
// - claim in store if it exists (only claim.DelegatorRewardIndexes and claim.Reward)
// - global index in store
// - function args: delegator address, validator address, shouldIncludeValidator flag
// - delegator's delegations and the corresponding validators
//
// outputs
// - sets or creates a claim
type SynchronizeHardDelegatorRewardTests struct {
unitTester
}
func TestSynchronizeHardDelegatorReward(t *testing.T) {
suite.Run(t, new(SynchronizeHardDelegatorRewardTests))
}
func (suite *SynchronizeHardDelegatorRewardTests) storeGlobalDelegatorFactor(rewardIndexes types.RewardIndexes) {
factor := rewardIndexes[0]
suite.keeper.SetHardDelegatorRewardFactor(suite.ctx, factor.CollateralType, factor.RewardFactor)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestClaimIndexesAreUnchangedWhenGlobalFactorUnchanged() {
delegator := arbitraryAddress()
stakingKeeper := fakeStakingKeeper{} // use an empty staking keeper that returns no delegations
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
claim := types.HardLiquidityProviderClaim{
BaseMultiClaim: types.BaseMultiClaim{
Owner: delegator,
},
DelegatorRewardIndexes: arbitraryDelegatorRewardIndexes,
}
suite.storeClaim(claim)
suite.storeGlobalDelegatorFactor(claim.DelegatorRewardIndexes)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(claim.DelegatorRewardIndexes, syncedClaim.DelegatorRewardIndexes)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestClaimIndexesAreUpdatedWhenGlobalFactorIncreased() {
delegator := arbitraryAddress()
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, fakeStakingKeeper{})
claim := types.HardLiquidityProviderClaim{
BaseMultiClaim: types.BaseMultiClaim{
Owner: delegator,
},
DelegatorRewardIndexes: arbitraryDelegatorRewardIndexes,
}
suite.storeClaim(claim)
globalIndexes := increaseRewardFactors(claim.DelegatorRewardIndexes)
suite.storeGlobalDelegatorFactor(globalIndexes)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(globalIndexes, syncedClaim.DelegatorRewardIndexes)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsUnchangedWhenGlobalFactorUnchanged() {
delegator := arbitraryAddress()
validatorAddress := arbitraryValidatorAddress()
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddress,
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddress),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
claim := types.HardLiquidityProviderClaim{
BaseMultiClaim: types.BaseMultiClaim{
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
}},
}
suite.storeClaim(claim)
suite.storeGlobalDelegatorFactor(claim.DelegatorRewardIndexes)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(claim.Reward, syncedClaim.Reward)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenNewRewardAdded() {
delegator := arbitraryAddress()
validatorAddress := arbitraryValidatorAddress()
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddress,
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddress),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
claim := types.HardLiquidityProviderClaim{
BaseMultiClaim: types.BaseMultiClaim{
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{},
}
suite.storeClaim(claim)
newGlobalIndexes := types.RewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
}}
suite.storeGlobalDelegatorFactor(newGlobalIndexes)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(newGlobalIndexes, syncedClaim.DelegatorRewardIndexes)
suite.Equal(
cs(c(types.HardLiquidityRewardDenom, 100)).Add(claim.Reward...),
syncedClaim.Reward,
)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestRewardIsIncreasedWhenGlobalFactorIncreased() {
delegator := arbitraryAddress()
validatorAddress := arbitraryValidatorAddress()
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddress,
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddress),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
claim := types.HardLiquidityProviderClaim{
BaseMultiClaim: types.BaseMultiClaim{
Owner: delegator,
Reward: arbitraryCoins(),
},
DelegatorRewardIndexes: types.RewardIndexes{{
CollateralType: types.BondDenom,
RewardFactor: d("0.1"),
}},
}
suite.storeClaim(claim)
suite.storeGlobalDelegatorFactor(
types.RewardIndexes{
types.NewRewardIndex(types.BondDenom, d("0.2")),
},
)
suite.keeper.SynchronizeHardDelegatorRewards(suite.ctx, claim.Owner, nil, false)
syncedClaim, _ := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, claim.Owner)
suite.Equal(
cs(c(types.HardLiquidityRewardDenom, 100)).Add(claim.Reward...),
syncedClaim.Reward,
)
}
func unslashedBondedValidator(address sdk.ValAddress) stakingtypes.Validator {
return stakingtypes.Validator{
OperatorAddress: address,
Status: sdk.Bonded,
// Set the tokens and shares equal so then
// a _delegator's_ token amount is equal to their shares amount
Tokens: i(1e12),
DelegatorShares: i(1e12).ToDec(),
}
}
func unslashedNotBondedValidator(address sdk.ValAddress) stakingtypes.Validator {
return stakingtypes.Validator{
OperatorAddress: address,
Status: sdk.Unbonding,
// Set the tokens and shares equal so then
// a _delegator's_ token amount is equal to their shares amount
Tokens: i(1e12),
DelegatorShares: i(1e12).ToDec(),
}
}
func (suite *SynchronizeHardDelegatorRewardTests) TestGetDelegatedWhenValAddrIsNil() {
// when valAddr is nil, get total delegated to bonded validators
delegator := arbitraryAddress()
validatorAddresses := generateValidatorAddresses(4)
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
//bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[0],
Shares: d("1"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[1],
Shares: d("10"),
},
// not bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[2],
Shares: d("100"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[3],
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddresses[0]),
unslashedBondedValidator(validatorAddresses[1]),
unslashedNotBondedValidator(validatorAddresses[2]),
unslashedNotBondedValidator(validatorAddresses[3]),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
suite.Equal(
d("11"), // delegation to bonded validators
suite.keeper.GetTotalDelegated(suite.ctx, delegator, nil, false),
)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestGetDelegatedWhenExcludingAValidator() {
// when valAddr is x, get total delegated to bonded validators excluding those to x
delegator := arbitraryAddress()
validatorAddresses := generateValidatorAddresses(4)
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
//bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[0],
Shares: d("1"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[1],
Shares: d("10"),
},
// not bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[2],
Shares: d("100"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[3],
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddresses[0]),
unslashedBondedValidator(validatorAddresses[1]),
unslashedNotBondedValidator(validatorAddresses[2]),
unslashedNotBondedValidator(validatorAddresses[3]),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
suite.Equal(
d("10"),
suite.keeper.GetTotalDelegated(suite.ctx, delegator, validatorAddresses[0], false),
)
}
func (suite *SynchronizeHardDelegatorRewardTests) TestGetDelegatedWhenIncludingAValidator() {
// when valAddr is x, get total delegated to bonded validators including those to x
delegator := arbitraryAddress()
validatorAddresses := generateValidatorAddresses(4)
stakingKeeper := fakeStakingKeeper{
delegations: stakingtypes.Delegations{
//bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[0],
Shares: d("1"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[1],
Shares: d("10"),
},
// not bonded
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[2],
Shares: d("100"),
},
{
DelegatorAddress: delegator,
ValidatorAddress: validatorAddresses[3],
Shares: d("1000"),
},
},
validators: stakingtypes.Validators{
unslashedBondedValidator(validatorAddresses[0]),
unslashedBondedValidator(validatorAddresses[1]),
unslashedNotBondedValidator(validatorAddresses[2]),
unslashedNotBondedValidator(validatorAddresses[3]),
},
}
suite.keeper = suite.NewKeeper(&fakeParamSubspace{}, nil, nil, nil, nil, stakingKeeper)
suite.Equal(
d("111"),
suite.keeper.GetTotalDelegated(suite.ctx, delegator, validatorAddresses[2], true),
)
}