Skip to content

Commit

Permalink
test: add voting power unit test and tally simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
dongsam committed Feb 17, 2022
1 parent aa648ce commit aced90b
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 259 deletions.
1 change: 1 addition & 0 deletions app/params/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ const (
DefaultWeightUpdateWhitelistValidatorsProposal int = 5
DefaultWeightDeleteWhitelistValidatorsProposal int = 5
DefaultWeightCompleteRedelegationUnbonding int = 30
DefaultWeightTallyWithLiquidStaking int = 30
)
2 changes: 1 addition & 1 deletion x/liquidstaking/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ func (h Hooks) AfterProposalVotingPeriodEnded(_ sdk.Context, _ uint64) {

// GetOtherVotes calculate the voting power of the person who participated in liquid staking.
func (h Hooks) GetOtherVotes(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) {
h.k.TallyLiquidGov(ctx, votes, otherVotes)
h.k.TallyLiquidStakingGov(ctx, votes, otherVotes)
}
248 changes: 0 additions & 248 deletions x/liquidstaking/keeper/liquidstaking_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package keeper_test

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
squadtypes "github.com/cosmosquad-labs/squad/types"
"github.com/cosmosquad-labs/squad/x/liquidstaking/types"
"github.com/k0kubun/pp"
)

// tests LiquidStaking, LiquidUnstaking
Expand Down Expand Up @@ -179,246 +174,3 @@ func (s *KeeperTestSuite) TestLiquidStaking() {
s.Require().Equal(nas.ProxyAccBalance, sdk.ZeroInt())
s.Require().Equal(nas.NetAmount, sdk.ZeroDec())
}

// test Liquid Staking gov power
func (s *KeeperTestSuite) TestLiquidStakingGov() {
params := types.DefaultParams()
liquidBondDenom := s.keeper.LiquidBondDenom(s.ctx)

// v1, v2, v3, v4
vals, valOpers, _ := s.CreateValidators([]int64{10000000, 10000000, 10000000, 10000000, 10000000})
params.WhitelistedValidators = []types.WhitelistedValidator{
{ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)},
{ValidatorAddress: valOpers[1].String(), TargetWeight: sdk.NewInt(10)},
{ValidatorAddress: valOpers[2].String(), TargetWeight: sdk.NewInt(10)},
{ValidatorAddress: valOpers[3].String(), TargetWeight: sdk.NewInt(10)},
}
s.keeper.SetParams(s.ctx, params)
s.keeper.UpdateLiquidValidatorSet(s.ctx)

liquidValidators := s.keeper.GetAllLiquidValidators(s.ctx)

val4, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[3])

delA := s.addrs[0]
delB := s.addrs[1]
delC := s.addrs[2]
delD := s.addrs[3]
delE := s.addrs[4]
delF := s.addrs[5]
delG := s.addrs[6]

_, err := s.app.StakingKeeper.Delegate(s.ctx, delG, sdk.NewInt(60000000), stakingtypes.Unbonded, val4, true)
s.Require().NoError(err)

// 7 addr B, C, D, E, F, G, H
tp := govtypes.NewTextProposal("Test", "description")
proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp)
s.Require().NoError(err)

proposal.Status = govtypes.StatusVotingPeriod
s.app.GovKeeper.SetProposal(s.ctx, proposal)

s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[1], govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[3], govtypes.NewNonSplitVoteOption(govtypes.OptionNo)))

s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionNo)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delC, govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delD, govtypes.NewNonSplitVoteOption(govtypes.OptionNoWithVeto)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delE, govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delF, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain)))
s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delG, govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))

s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String())
return false
})

assertTallyResult := func(yes, no, vito, abstain int64) {
cachedCtx, _ := s.ctx.CacheContext()
_, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(yes), result.Yes)
s.Require().Equal(sdk.NewInt(no), result.No)
s.Require().Equal(sdk.NewInt(vito), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(abstain), result.Abstain)
}

assertTallyResult(80000000, 10000000, 0, 0)

delAbToken := sdk.NewInt(40000000)
delBbToken := sdk.NewInt(80000000)
delCbToken := sdk.NewInt(60000000)
delDbToken := sdk.NewInt(20000000)
delEbToken := sdk.NewInt(80000000)
delFbToken := sdk.NewInt(120000000)
newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delA, sdk.NewCoin(sdk.DefaultBondDenom, delAbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delA, liquidBondDenom).Amount, delAbToken)

newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, delBbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, liquidBondDenom).Amount, delBbToken)

newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delC, sdk.NewCoin(sdk.DefaultBondDenom, delCbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delC, liquidBondDenom).Amount, delCbToken)

newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delD, sdk.NewCoin(sdk.DefaultBondDenom, delDbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delD, liquidBondDenom).Amount, delDbToken)

newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delE, sdk.NewCoin(sdk.DefaultBondDenom, delEbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delE, liquidBondDenom).Amount, delEbToken)

newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delF, sdk.NewCoin(sdk.DefaultBondDenom, delFbToken))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delF, liquidBondDenom).Amount, delFbToken)

totalPower := sdk.ZeroInt()
totalShare := sdk.ZeroDec()
s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String())
totalPower = totalPower.Add(validator.GetTokens())
totalShare = totalShare.Add(validator.GetDelegatorShares())
return false
})

assertTallyResult(240000000, 100000000, 20000000, 120000000)

// Test TallyLiquidGov
otherVotes := make(govtypes.OtherVotes)
testOtherVotes := func(voter sdk.AccAddress, bTokenValue sdk.Int) {
s.Require().Len(otherVotes[voter.String()], liquidValidators.Len())
totalVotingPower := sdk.ZeroDec()
for _, v := range liquidValidators {
votingPower := otherVotes[voter.String()][v.OperatorAddress]
totalVotingPower = totalVotingPower.Add(votingPower)
// equal when all liquid validator has same currentWeight
s.Require().EqualValues(votingPower, bTokenValue.ToDec().QuoInt64(int64(liquidValidators.Len())))
}
s.Require().EqualValues(totalVotingPower.TruncateInt(), s.keeper.CalcLiquidStakingVotingPower(s.ctx, voter))
}
tallyLiquidGov := func() {
cachedCtx, _ := s.ctx.CacheContext()
otherVotes = make(govtypes.OtherVotes)
votes := s.app.GovKeeper.GetVotes(cachedCtx, proposal.ProposalId)
s.keeper.TallyLiquidGov(cachedCtx, &votes, &otherVotes)
squadtypes.PP(otherVotes)

s.Require().Len(otherVotes, 5)
testOtherVotes(delB, delBbToken)
testOtherVotes(delC, delCbToken)
testOtherVotes(delD, delDbToken)
testOtherVotes(delE, delEbToken)
testOtherVotes(delF, delFbToken)
}

tallyLiquidGov()

// Test balance of PoolTokens including bToken
pair1 := s.createPair(delB, params.LiquidBondDenom, sdk.DefaultBondDenom, false)
pool1 := s.createPool(delB, pair1.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false)
tallyLiquidGov()
pair2 := s.createPair(delC, sdk.DefaultBondDenom, params.LiquidBondDenom, false)
pool2 := s.createPool(delC, pair2.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false)
balance := s.app.BankKeeper.GetBalance(s.ctx, delC, pool2.PoolCoinDenom)
fmt.Println(balance)
tallyLiquidGov()

// Test Farming Queued Staking of bToken
s.CreateFixedAmountPlan(s.addrs[0], map[string]string{params.LiquidBondDenom: "0.4", pool1.PoolCoinDenom: "0.3", pool2.PoolCoinDenom: "0.3"}, map[string]int64{"testdenom": 1})
s.Stake(delD, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(10000000))))
queuedStaking, found := s.app.FarmingKeeper.GetQueuedStaking(s.ctx, params.LiquidBondDenom, delD)
s.True(found)
s.Equal(queuedStaking.Amount, sdk.NewInt(10000000))
tallyLiquidGov()

// Test Farming Staking Position Staking of bToken
s.AdvanceEpoch()
staking, found := s.app.FarmingKeeper.GetStaking(s.ctx, params.LiquidBondDenom, delD)
s.True(found)
s.Equal(staking.Amount, sdk.NewInt(10000000))
tallyLiquidGov()

// Test Farming Queued Staking of PoolTokens including bToken
s.Stake(delC, sdk.NewCoins(sdk.NewCoin(pool2.PoolCoinDenom, sdk.NewInt(10000000))))
queuedStaking, found = s.app.FarmingKeeper.GetQueuedStaking(s.ctx, pool2.PoolCoinDenom, delC)
s.True(found)
s.Equal(queuedStaking.Amount, sdk.NewInt(10000000))
tallyLiquidGov()

// Test Farming Staking Position of PoolTokens including bToken
s.AdvanceEpoch()
staking, found = s.app.FarmingKeeper.GetStaking(s.ctx, pool2.PoolCoinDenom, delC)
s.True(found)
s.Equal(staking.Amount, sdk.NewInt(10000000))
tallyLiquidGov()
}

// test Liquid Staking gov power
func (s *KeeperTestSuite) TestLiquidStakingGov2() {
params := types.DefaultParams()

vals, valOpers, _ := s.CreateValidators([]int64{10000000})
params.WhitelistedValidators = []types.WhitelistedValidator{
{ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)},
}
s.keeper.SetParams(s.ctx, params)
s.keeper.UpdateLiquidValidatorSet(s.ctx)

val1, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[0])

delA := s.addrs[0]
delB := s.addrs[1]

_, err := s.app.StakingKeeper.Delegate(s.ctx, delA, sdk.NewInt(50000000), stakingtypes.Unbonded, val1, true)
s.Require().NoError(err)

tp := govtypes.NewTextProposal("Test", "description")
proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp)
s.Require().NoError(err)

proposal.Status = govtypes.StatusVotingPeriod
s.app.GovKeeper.SetProposal(s.ctx, proposal)

s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delA, govtypes.NewNonSplitVoteOption(govtypes.OptionYes)))

cachedCtx, _ := s.ctx.CacheContext()
_, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(50000000), result.Yes)
s.Require().Equal(sdk.NewInt(0), result.No)
s.Require().Equal(sdk.NewInt(0), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(0), result.Abstain)

s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionNo)))
cachedCtx, _ = s.ctx.CacheContext()
_, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(50000000), result.Yes)
s.Require().Equal(sdk.NewInt(10000000), result.No)
s.Require().Equal(sdk.NewInt(0), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(0), result.Abstain)

newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50000000)))
s.Require().NoError(err)
s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, params.LiquidBondDenom).Amount, sdk.NewInt(50000000))

cachedCtx, _ = s.ctx.CacheContext()
_, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(50000000), result.Yes)
s.Require().Equal(sdk.NewInt(60000000), result.No)
s.Require().Equal(sdk.NewInt(0), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(0), result.Abstain)

s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain)))

cachedCtx, _ = s.ctx.CacheContext()
_, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(50000000), result.Yes)
s.Require().Equal(sdk.NewInt(10000000), result.No)
s.Require().Equal(sdk.NewInt(0), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(50000000), result.Abstain)
}

// TODO: tally bonded validator case
3 changes: 1 addition & 2 deletions x/liquidstaking/keeper/tally.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ func (k Keeper) CalcStakingVotingPower(ctx sdk.Context, addr sdk.AccAddress) sdk
}

// CalcLiquidStakingVotingPower returns voting power of the addr by liquid bond denom
// TODO: refactor votingPowerStruct (delShares, btoken, poolCoin, farming)
func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddress) sdk.Int {
liquidBondDenom := k.LiquidBondDenom(ctx)

Expand Down Expand Up @@ -169,7 +168,7 @@ func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddres
}
}

func (k Keeper) TallyLiquidGov(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) {
func (k Keeper) TallyLiquidStakingGov(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) {
liquidBondDenom := k.LiquidBondDenom(ctx)

// skip when no liquid bond token supply
Expand Down
Loading

0 comments on commit aced90b

Please sign in to comment.