2018-10-04 18:37:40 -07:00
|
|
|
package keeper
|
|
|
|
|
2018-10-09 10:58:59 -07:00
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
2018-10-10 14:43:47 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2018-10-09 10:58:59 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
2018-10-04 18:37:40 -07:00
|
|
|
|
2018-10-10 23:14:06 -07:00
|
|
|
func TestWithdrawDelegationRewardBasic(t *testing.T) {
|
|
|
|
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
|
2018-10-09 10:58:59 -07:00
|
|
|
stakeHandler := stake.NewHandler(sk)
|
|
|
|
denom := sk.GetParams(ctx).BondDenom
|
|
|
|
|
|
|
|
//first make a validator
|
2018-10-10 14:43:47 -07:00
|
|
|
msgCreateValidator := stake.NewTestMsgCreateValidator(valOpAddr1, valConsPk1, 10)
|
2018-10-09 10:58:59 -07:00
|
|
|
got := stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
2018-10-10 14:43:47 -07:00
|
|
|
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
|
2018-10-09 10:58:59 -07:00
|
|
|
|
|
|
|
// delegate
|
2018-10-10 14:43:47 -07:00
|
|
|
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
|
2018-10-09 10:58:59 -07:00
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
2018-10-09 18:43:23 -07:00
|
|
|
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(90), amt.Int64())
|
2018-10-09 18:59:07 -07:00
|
|
|
|
2018-10-10 23:14:06 -07:00
|
|
|
// allocate 100 denom of fees
|
|
|
|
feeInputs := sdk.NewInt(100)
|
2018-10-10 14:43:47 -07:00
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-10 14:43:47 -07:00
|
|
|
|
2018-10-10 23:14:06 -07:00
|
|
|
// withdraw delegation
|
|
|
|
ctx = ctx.WithBlockHeight(1)
|
2018-10-10 14:43:47 -07:00
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
2018-10-10 23:14:06 -07:00
|
|
|
|
2018-10-12 02:37:30 -07:00
|
|
|
expRes := sdk.NewDec(90).Add(sdk.NewDec(100).Quo(sdk.NewDec(2))).TruncateInt() // 90 + 100 tokens * 10/20
|
|
|
|
require.True(sdk.IntEq(t, expRes, amt))
|
2018-10-10 23:14:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestWithdrawDelegationRewardWithCommission(t *testing.T) {
|
2018-10-10 23:26:54 -07:00
|
|
|
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
|
|
|
|
stakeHandler := stake.NewHandler(sk)
|
|
|
|
denom := sk.GetParams(ctx).BondDenom
|
|
|
|
|
|
|
|
//first make a validator with 10% commission
|
|
|
|
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
|
|
|
|
got := stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
|
|
|
|
|
|
// delegate
|
|
|
|
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
|
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
|
|
|
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(90), amt.Int64())
|
2018-10-10 23:14:06 -07:00
|
|
|
|
2018-10-10 23:26:54 -07:00
|
|
|
// allocate 100 denom of fees
|
|
|
|
feeInputs := sdk.NewInt(100)
|
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-10 23:26:54 -07:00
|
|
|
|
|
|
|
// withdraw delegation
|
|
|
|
ctx = ctx.WithBlockHeight(1)
|
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
|
2018-10-12 02:37:30 -07:00
|
|
|
expRes := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(2))).TruncateInt() // 90 + 100*90% tokens * 10/20
|
|
|
|
require.True(sdk.IntEq(t, expRes, amt))
|
2018-10-04 18:37:40 -07:00
|
|
|
}
|
|
|
|
|
2018-10-12 01:11:09 -07:00
|
|
|
func TestWithdrawDelegationRewardTwoDelegators(t *testing.T) {
|
2018-10-12 02:37:30 -07:00
|
|
|
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
|
|
|
|
stakeHandler := stake.NewHandler(sk)
|
|
|
|
denom := sk.GetParams(ctx).BondDenom
|
|
|
|
|
|
|
|
//first make a validator with 10% commission
|
|
|
|
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
|
|
|
|
got := stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
|
|
|
|
|
|
// delegate
|
|
|
|
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
|
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
|
|
|
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(90), amt.Int64())
|
|
|
|
|
|
|
|
msgDelegate = stake.NewTestMsgDelegate(delAddr2, valOpAddr1, 20)
|
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(80), amt.Int64())
|
|
|
|
|
|
|
|
// allocate 100 denom of fees
|
|
|
|
feeInputs := sdk.NewInt(100)
|
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-12 02:37:30 -07:00
|
|
|
|
|
|
|
// delegator 1 withdraw delegation
|
|
|
|
ctx = ctx.WithBlockHeight(1)
|
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
|
|
|
|
expRes := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(4))).TruncateInt() // 90 + 100*90% tokens * 10/40
|
|
|
|
require.True(sdk.IntEq(t, expRes, amt))
|
|
|
|
}
|
|
|
|
|
|
|
|
// this test demonstrates how two delegators with the same power can end up
|
|
|
|
// with different rewards in the end
|
|
|
|
func TestWithdrawDelegationRewardTwoDelegatorsUneven(t *testing.T) {
|
|
|
|
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
|
|
|
|
stakeHandler := stake.NewHandler(sk)
|
|
|
|
denom := sk.GetParams(ctx).BondDenom
|
|
|
|
|
|
|
|
//first make a validator with no commission
|
|
|
|
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr1, valConsPk1, 10, sdk.ZeroDec())
|
|
|
|
got := stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
|
|
|
|
|
|
// delegate
|
|
|
|
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
|
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
|
|
|
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(90), amt.Int64())
|
|
|
|
|
|
|
|
msgDelegate = stake.NewTestMsgDelegate(delAddr2, valOpAddr1, 10)
|
|
|
|
got = stakeHandler(ctx, msgDelegate)
|
|
|
|
require.True(t, got.IsOK())
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(90), amt.Int64())
|
|
|
|
|
|
|
|
// allocate 100 denom of fees
|
|
|
|
feeInputs := sdk.NewInt(90)
|
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-12 02:37:30 -07:00
|
|
|
ctx = ctx.WithBlockHeight(1)
|
|
|
|
|
|
|
|
// delegator 1 withdraw delegation early, delegator 2 just keeps it's accum
|
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
|
|
|
|
expRes1 := sdk.NewDec(90).Add(sdk.NewDec(90).Quo(sdk.NewDec(3))).TruncateInt() // 90 + 100 * 10/30
|
|
|
|
require.True(sdk.IntEq(t, expRes1, amt))
|
|
|
|
|
|
|
|
// allocate 200 denom of fees
|
|
|
|
feeInputs = sdk.NewInt(180)
|
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-12 02:37:30 -07:00
|
|
|
ctx = ctx.WithBlockHeight(2)
|
|
|
|
|
|
|
|
// delegator 2 now withdraws everything it's entitled to
|
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr2, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr2).GetCoins().AmountOf(denom)
|
|
|
|
// existingTokens + (100+200 * (10/(20+30))
|
|
|
|
withdrawnFromVal := sdk.NewDec(60 + 180).Mul(sdk.NewDec(2)).Quo(sdk.NewDec(5))
|
|
|
|
expRes2 := sdk.NewDec(90).Add(withdrawnFromVal).TruncateInt()
|
|
|
|
require.True(sdk.IntEq(t, expRes2, amt))
|
|
|
|
|
|
|
|
// finally delegator 1 withdraws the remainder of its reward
|
|
|
|
keeper.WithdrawDelegationReward(ctx, delAddr1, valOpAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
|
|
|
|
remainingInVal := sdk.NewDec(60 + 180).Sub(withdrawnFromVal)
|
|
|
|
expRes3 := sdk.NewDecFromInt(expRes1).Add(remainingInVal.Mul(sdk.NewDec(1)).Quo(sdk.NewDec(3))).TruncateInt()
|
|
|
|
require.True(sdk.IntEq(t, expRes3, amt))
|
2018-10-04 18:37:40 -07:00
|
|
|
|
2018-10-12 02:37:30 -07:00
|
|
|
// verify the final withdraw amounts are different
|
|
|
|
require.True(t, expRes2.GT(expRes3))
|
2018-10-04 18:37:40 -07:00
|
|
|
}
|
|
|
|
|
2018-10-12 01:11:09 -07:00
|
|
|
func TestWithdrawDelegationRewardsAll(t *testing.T) {
|
|
|
|
ctx, accMapper, keeper, sk, fck := CreateTestInputAdvanced(t, false, 100, sdk.ZeroDec())
|
|
|
|
stakeHandler := stake.NewHandler(sk)
|
|
|
|
denom := sk.GetParams(ctx).BondDenom
|
|
|
|
|
|
|
|
//make some validators with different commissions
|
|
|
|
msgCreateValidator := stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr1, valConsPk1, 10, sdk.NewDecWithPrec(1, 1))
|
|
|
|
got := stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
|
|
|
|
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr2, valConsPk2, 50, sdk.NewDecWithPrec(2, 1))
|
|
|
|
got = stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
|
|
|
|
msgCreateValidator = stake.NewTestMsgCreateValidatorWithCommission(
|
|
|
|
valOpAddr3, valConsPk3, 40, sdk.NewDecWithPrec(3, 1))
|
|
|
|
got = stakeHandler(ctx, msgCreateValidator)
|
|
|
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
|
|
|
|
|
|
|
_ = sk.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
|
|
|
|
|
|
// delegate to all the validators
|
|
|
|
msgDelegate := stake.NewTestMsgDelegate(delAddr1, valOpAddr1, 10)
|
|
|
|
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
|
|
|
|
msgDelegate = stake.NewTestMsgDelegate(delAddr1, valOpAddr2, 20)
|
|
|
|
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
|
|
|
|
msgDelegate = stake.NewTestMsgDelegate(delAddr1, valOpAddr3, 30)
|
|
|
|
require.True(t, stakeHandler(ctx, msgDelegate).IsOK())
|
|
|
|
|
|
|
|
// 40 tokens left after delegating 60 of them
|
|
|
|
amt := accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
|
|
|
require.Equal(t, int64(40), amt.Int64())
|
|
|
|
|
|
|
|
// total power of each validator:
|
|
|
|
// validator 1: 10 (self) + 10 (delegator) = 20
|
|
|
|
// validator 2: 50 (self) + 20 (delegator) = 70
|
|
|
|
// validator 3: 40 (self) + 30 (delegator) = 70
|
|
|
|
// grand total: 160
|
|
|
|
|
|
|
|
// allocate 100 denom of fees
|
|
|
|
feeInputs := sdk.NewInt(1000)
|
|
|
|
fck.SetCollectedFees(sdk.Coins{sdk.NewCoin(denom, feeInputs)})
|
|
|
|
require.Equal(t, feeInputs, fck.GetCollectedFees(ctx).AmountOf(denom))
|
2018-10-15 19:09:13 -07:00
|
|
|
keeper.AllocateFees(ctx, sdk.OneDec(), valConsAddr1)
|
2018-10-12 01:11:09 -07:00
|
|
|
|
|
|
|
// withdraw delegation
|
|
|
|
ctx = ctx.WithBlockHeight(1)
|
|
|
|
keeper.WithdrawDelegationRewardsAll(ctx, delAddr1)
|
|
|
|
amt = accMapper.GetAccount(ctx, delAddr1).GetCoins().AmountOf(denom)
|
2018-10-04 18:37:40 -07:00
|
|
|
|
2018-10-12 01:11:09 -07:00
|
|
|
// orig-amount + fees *(1-proposerReward)* (val1Portion * delegatorPotion * (1-val1Commission) ... etc)
|
|
|
|
// + fees *(proposerReward) * (delegatorPotion * (1-val1Commission))
|
|
|
|
// 40 + 1000 *(1- 0.95)* (20/160 * 10/20 * 0.9 + 70/160 * 20/70 * 0.8 + 70/160 * 30/70 * 0.7)
|
|
|
|
// 40 + 1000 *( 0.05) * (10/20 * 0.9)
|
|
|
|
feesInNonProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(95, 2))
|
|
|
|
feesInProposer := sdk.NewDecFromInt(feeInputs).Mul(sdk.NewDecWithPrec(5, 2))
|
|
|
|
feesInVal1 := feesInNonProposer.Mul(sdk.NewDec(10).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(9, 1))
|
|
|
|
feesInVal2 := feesInNonProposer.Mul(sdk.NewDec(20).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(8, 1))
|
|
|
|
feesInVal3 := feesInNonProposer.Mul(sdk.NewDec(30).Quo(sdk.NewDec(160))).Mul(sdk.NewDecWithPrec(7, 1))
|
|
|
|
feesInVal1Proposer := feesInProposer.Mul(sdk.NewDec(10).Quo(sdk.NewDec(20))).Mul(sdk.NewDecWithPrec(9, 1))
|
|
|
|
expRes := sdk.NewDec(40).Add(feesInVal1).Add(feesInVal2).Add(feesInVal3).Add(feesInVal1Proposer).TruncateInt()
|
|
|
|
require.True(sdk.IntEq(t, expRes, amt))
|
2018-10-04 18:37:40 -07:00
|
|
|
}
|