From 0024a0bf44dc865dec773bc6cfeea1833f2e0a5e Mon Sep 17 00:00:00 2001 From: Facundo Medica <14063057+facundomedica@users.noreply.github.com> Date: Mon, 29 Aug 2022 20:15:51 -0300 Subject: [PATCH] refactor: use mocks for x/distribution (#12889) * initial commit * add mocks * TestAllocateTokensToManyValidators * one file more in, like a hundred left * progress * progress * move tests to integration * finally got TestCalculateRewardsMultiDelegator right lol * small fixes * progress * progress * progress * progress * revert test panic * progress * progress * more progress * fix go.mod * merge * merge * cache issue? * fix go.sum * fix tests * make mocks * move back simulations Co-authored-by: Marko --- scripts/mockgen.sh | 1 + .../distribution/keeper/allocation_test.go | 248 +++++ .../distribution/keeper/common_test.go | 21 + .../distribution/keeper/delegation_test.go | 824 +++++++++++++++++ .../distribution/keeper/grpc_query_test.go | 2 +- .../distribution/keeper/keeper_test.go | 186 ++++ .../distribution/keeper/msg_server_test.go | 0 .../distribution/keeper/params_test.go | 117 +++ .../integration}/distribution/module_test.go | 0 x/distribution/keeper/allocation_test.go | 256 +++--- x/distribution/keeper/common_test.go | 7 +- x/distribution/keeper/delegation_test.go | 860 +++++++++++------- x/distribution/keeper/keeper_test.go | 219 ++--- x/distribution/keeper/params_test.go | 52 +- .../testutil/expected_keepers_mocks.go | 376 ++++++++ x/distribution/testutil/staking_helper.go | 128 +++ .../simulation/decoder_test.go | 0 .../simulation/operations_test.go | 27 +- x/staking/simulation/operations_test.go | 2 +- 19 files changed, 2730 insertions(+), 596 deletions(-) create mode 100644 tests/integration/distribution/keeper/allocation_test.go create mode 100644 tests/integration/distribution/keeper/common_test.go create mode 100644 tests/integration/distribution/keeper/delegation_test.go rename {x => tests/integration}/distribution/keeper/grpc_query_test.go (99%) create mode 100644 tests/integration/distribution/keeper/keeper_test.go rename {x => tests/integration}/distribution/keeper/msg_server_test.go (100%) create mode 100644 tests/integration/distribution/keeper/params_test.go rename {x => tests/integration}/distribution/module_test.go (100%) create mode 100644 x/distribution/testutil/expected_keepers_mocks.go create mode 100644 x/distribution/testutil/staking_helper.go rename x/{distribution => }/simulation/decoder_test.go (100%) rename x/{distribution => }/simulation/operations_test.go (95%) diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index 66f5326489a4..7b57d0ce7a0b 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -21,6 +21,7 @@ $mockgen_cmd -source=x/authz/expected_keepers.go -package testutil -destination $mockgen_cmd -source=x/bank/types/expected_keepers.go -package testutil -destination x/bank/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/group/testutil/expected_keepers.go -package testutil -destination x/group/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/evidence/types/expected_keepers.go -package testutil -destination x/evidence/testutil/expected_keepers_mocks.go +$mockgen_cmd -source=x/distribution/types/expected_keepers.go -package testutil -destination x/distribution/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/slashing/types/expected_keepers.go -package testutil -destination x/slashing/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/genutil/types/expected_keepers.go -package testutil -destination x/genutil/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/gov/testutil/expected_keepers.go -package testutil -destination x/gov/testutil/expected_keepers_mocks.go diff --git a/tests/integration/distribution/keeper/allocation_test.go b/tests/integration/distribution/keeper/allocation_test.go new file mode 100644 index 000000000000..057935fc0bb1 --- /dev/null +++ b/tests/integration/distribution/keeper/allocation_test.go @@ -0,0 +1,248 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestAllocateTokensToValidatorWithCommission(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(sdk.ValAddress(addrs[0]), valConsPk0, sdk.NewInt(100), true) + val := stakingKeeper.Validator(ctx, valAddrs[0]) + + // allocate tokens + tokens := sdk.DecCoins{ + {Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(10)}, + } + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // check commission + expected := sdk.DecCoins{ + {Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(5)}, + } + require.Equal(t, expected, distrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()).Commission) + + // check current rewards + require.Equal(t, expected, distrKeeper.GetValidatorCurrentRewards(ctx, val.GetOperator()).Rewards) +} + +func TestAllocateTokensToManyValidators(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + + addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1234)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // create second validator with 0% commission + tstaking.Commission = stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[1], valConsPk1, sdk.NewInt(100), true) + + abciValA := abci.Validator{ + Address: valConsPk0.Address(), + Power: 100, + } + abciValB := abci.Validator{ + Address: valConsPk1.Address(), + Power: 100, + } + + // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) + + // allocate tokens as if both had voted and second was proposer + fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) + feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) + require.NotNil(t, feeCollector) + + // fund fee collector + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) + + accountKeeper.SetAccount(ctx, feeCollector) + + votes := []abci.VoteInfo{ + { + Validator: abciValA, + SignedLastBlock: true, + }, + { + Validator: abciValB, + SignedLastBlock: true, + }, + } + distrKeeper.AllocateTokens(ctx, 200, votes) + + // 98 outstanding rewards (100 less 2 to community pool) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards) + + // 2 community pool coins + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, distrKeeper.GetFeePool(ctx).CommunityPool) + + // 50% commission for first proposer, (0.5 * 98%) * 100 / 2 = 23.25 + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + + // zero commission for second proposer + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + + // just staking.proportional for first proposer less commission = (0.5 * 98%) * 100 / 2 = 24.50 + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards) + + // proposer reward + staking.proportional for second proposer = (0.5 * (98%)) * 100 = 49 + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards) +} + +func TestAllocateTokensTruncation(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + + addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 10% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(110), true) + + // create second validator with 10% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[1], valConsPk1, sdk.NewInt(100), true) + + // create third validator with 10% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[2], valConsPk2, sdk.NewInt(100), true) + + abciValA := abci.Validator{ + Address: valConsPk0.Address(), + Power: 11, + } + abciValB := abci.Validator{ + Address: valConsPk1.Address(), + Power: 10, + } + abciValС := abci.Validator{ + Address: valConsPk2.Address(), + Power: 10, + } + + // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) + + // allocate tokens as if both had voted and second was proposer + fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840))) + + feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) + require.NotNil(t, feeCollector) + + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) + + accountKeeper.SetAccount(ctx, feeCollector) + + votes := []abci.VoteInfo{ + { + Validator: abciValA, + SignedLastBlock: true, + }, + { + Validator: abciValB, + SignedLastBlock: true, + }, + { + Validator: abciValС, + SignedLastBlock: true, + }, + } + distrKeeper.AllocateTokens(ctx, 31, votes) + + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsValid()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsValid()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[2]).Rewards.IsValid()) +} diff --git a/tests/integration/distribution/keeper/common_test.go b/tests/integration/distribution/keeper/common_test.go new file mode 100644 index 000000000000..f5d7ff33b858 --- /dev/null +++ b/tests/integration/distribution/keeper/common_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +var ( + PKS = simtestutil.CreateTestPubKeys(5) + + valConsPk0 = PKS[0] + valConsPk1 = PKS[1] + valConsPk2 = PKS[2] + + valConsAddr0 = sdk.ConsAddress(valConsPk0.Address()) + valConsAddr1 = sdk.ConsAddress(valConsPk1.Address()) + + distrAcc = authtypes.NewEmptyModuleAccount(types.ModuleName) +) diff --git a/tests/integration/distribution/keeper/delegation_test.go b/tests/integration/distribution/keeper/delegation_test.go new file mode 100644 index 000000000000..14a3504ca621 --- /dev/null +++ b/tests/integration/distribution/keeper/delegation_test.go @@ -0,0 +1,824 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestCalculateRewardsBasic(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator and start new block + staking.EndBlocker(ctx, stakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + tstaking.Ctx = ctx + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // historical count should be 2 (once for validator init, once for delegation init) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // historical count should be 2 still + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // allocate some rewards + initial := int64(10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsAfterSlash(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valPower := int64(100) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // retrieve validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsAfterManySlashes(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator by 50% again + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegator(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + initial := int64(20) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // second delegation + tstaking.Ctx = ctx + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 3/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 3 / 4)}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 1 / 4)}}, rewards) + + // commission should be equal to initial (50% twice) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestWithdrawDelegationRewardsBasic(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + balancePower := int64(1000) + balanceTokens := stakingKeeper.TokensFromConsensusPower(ctx, balancePower) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + // create validator with 50% commission + power := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valTokens := tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, power, true) + + // assert correct initial balance + expTokens := balanceTokens.Sub(valTokens) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, expTokens)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + + // allocate some rewards + initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)} + + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // historical count should be 2 (initial + latest for delegation) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // withdraw rewards + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.Nil(t, err) + + // historical count should still be 2 (added one record, cleared one) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // assert correct balance + exp := balanceTokens.Sub(valTokens).Add(initial.QuoRaw(2)) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) + + // withdraw commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.Nil(t, err) + + // assert correct balance + exp = balanceTokens.Sub(valTokens).Add(initial) + require.Equal(t, + sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, + bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), + ) +} + +func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be zero + require.True(t, rewards.IsZero()) + + // start out block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some rewards + initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 10)) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator by 50% + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + + // slash the validator by 50% again + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) + + // fetch the validator again + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // increase block height + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + // rewards should be half the tokens + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards) + + // commission should be the other half + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valPower := int64(100) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk0, valPower, true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 30)) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // second delegation + tstaking.DelegateWithPower(sdk.AccAddress(valAddrs[1]), valAddrs[0], 100) + + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // slash the validator again + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be initial / 3 + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards) + + // commission should be equal to initial (twice 50% commission, unaffected by slashing) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) +} + +func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + initial := int64(20) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator and delegation + val := stakingKeeper.Validator(ctx, valAddrs[0]) + del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // allocate some rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // historical count should be 2 (validator init, delegation init) + require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // second delegation + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + + // historical count should be 3 (second delegation init) + require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // fetch updated validator + val = stakingKeeper.Validator(ctx, valAddrs[0]) + del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // first delegator withdraws + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + // second delegator withdraws + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + require.NoError(t, err) + + // historical count should be 3 (validator init + two delegations) + require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + + // validator withdraws commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be zero + require.True(t, rewards.IsZero()) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be zero + require.True(t, rewards.IsZero()) + + // commission should be zero + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // first delegator withdraws again + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be zero + require.True(t, rewards.IsZero()) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) + + // commission should be half initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // withdraw commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) + + // end period + endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) + + // calculate delegation rewards for del1 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + + // rewards for del1 should be 1/4 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) + + // calculate delegation rewards for del2 + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + + // rewards for del2 should be 1/2 initial + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) + + // commission should be zero + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) +} + +func Test100PercentCommissionReward(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + initial := int64(20) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) + accountKeeper.SetModuleAccount(ctx, distrAcc) + + tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + + // create validator with 100% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) + stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + + // end block to bond validator + staking.EndBlocker(ctx, stakingKeeper) + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // fetch validator + val := stakingKeeper.Validator(ctx, valAddrs[0]) + + // allocate some rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // allocate some more rewards + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) + + denom, _ := sdk.GetBaseDenom() + zeroRewards := sdk.Coins{ + sdk.Coin{ + Denom: denom, + Amount: math.ZeroInt(), + }, + } + require.True(t, rewards.IsEqual(zeroRewards)) + events := ctx.EventManager().Events() + lastEvent := events[len(events)-1] + hasValue := false + for _, attr := range lastEvent.Attributes { + if string(attr.Key) == "amount" && string(attr.Value) == "0" { + hasValue = true + } + } + require.True(t, hasValue) +} diff --git a/x/distribution/keeper/grpc_query_test.go b/tests/integration/distribution/keeper/grpc_query_test.go similarity index 99% rename from x/distribution/keeper/grpc_query_test.go rename to tests/integration/distribution/keeper/grpc_query_test.go index 60dd9648d8d6..aa4c09c1ecf8 100644 --- a/x/distribution/keeper/grpc_query_test.go +++ b/tests/integration/distribution/keeper/grpc_query_test.go @@ -364,7 +364,7 @@ func (suite *KeeperTestSuite) TestGRPCDelegationRewards() { tstaking := teststaking.NewHelper(suite.T(), ctx, suite.stakingKeeper) tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) staking.EndBlocker(ctx, suite.stakingKeeper) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) diff --git a/tests/integration/distribution/keeper/keeper_test.go b/tests/integration/distribution/keeper/keeper_test.go new file mode 100644 index 000000000000..009bddd4a157 --- /dev/null +++ b/tests/integration/distribution/keeper/keeper_test.go @@ -0,0 +1,186 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" +) + +func TestSetWithdrawAddr(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + + params := distrKeeper.GetParams(ctx) + params.WithdrawAddrEnabled = false + require.NoError(t, distrKeeper.SetParams(ctx, params)) + + err = distrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) + require.NotNil(t, err) + + params.WithdrawAddrEnabled = true + require.NoError(t, distrKeeper.SetParams(ctx, params)) + + err = distrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) + require.Nil(t, err) + + require.Error(t, distrKeeper.SetWithdrawAddr(ctx, addr[0], distrAcc.GetAddress())) +} + +func TestWithdrawValidatorCommission(t *testing.T) { + var ( + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &accountKeeper, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valCommission := sdk.DecCoins{ + sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(5).Quo(math.LegacyNewDec(4))), + sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(3).Quo(math.LegacyNewDec(2))), + } + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + // set module account coins + distrAcc := distrKeeper.GetDistributionAccount(ctx) + coins := sdk.NewCoins(sdk.NewCoin("mytoken", sdk.NewInt(2)), sdk.NewCoin("stake", sdk.NewInt(2))) + require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), coins)) + + accountKeeper.SetModuleAccount(ctx, distrAcc) + + // check initial balance + balance := bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) + expTokens := stakingKeeper.TokensFromConsensusPower(ctx, 1000) + expCoins := sdk.NewCoins(sdk.NewCoin("stake", expTokens)) + require.Equal(t, expCoins, balance) + + // set outstanding rewards + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) + + // set commission + distrKeeper.SetValidatorAccumulatedCommission(ctx, valAddrs[0], types.ValidatorAccumulatedCommission{Commission: valCommission}) + + // withdraw commission + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) + + // check balance increase + balance = bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) + require.Equal(t, sdk.NewCoins( + sdk.NewCoin("mytoken", sdk.NewInt(1)), + sdk.NewCoin("stake", expTokens.AddRaw(1)), + ), balance) + + // check remainder + remainder := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission + require.Equal(t, sdk.DecCoins{ + sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(1).Quo(math.LegacyNewDec(4))), + sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(2))), + }, remainder) + + require.True(t, true) +} + +func TestGetTotalRewards(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valCommission := sdk.DecCoins{ + sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(5).Quo(math.LegacyNewDec(4))), + sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(3).Quo(math.LegacyNewDec(2))), + } + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[1], types.ValidatorOutstandingRewards{Rewards: valCommission}) + + expectedRewards := valCommission.MulDec(math.LegacyNewDec(2)) + totalRewards := distrKeeper.GetTotalRewards(ctx) + + require.Equal(t, expectedRewards, totalRewards) +} + +func TestFundCommunityPool(t *testing.T) { + var ( + bankKeeper bankkeeper.Keeper + distrKeeper keeper.Keeper + stakingKeeper *stakingkeeper.Keeper + ) + + app, err := simtestutil.Setup(testutil.AppConfig, + &bankKeeper, + &distrKeeper, + &stakingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + // reset fee pool + distrKeeper.SetFeePool(ctx, types.InitialFeePool()) + + addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, math.ZeroInt()) + + amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + require.NoError(t, banktestutil.FundAccount(bankKeeper, ctx, addr[0], amount)) + + initPool := distrKeeper.GetFeePool(ctx) + require.Empty(t, initPool.CommunityPool) + + err = distrKeeper.FundCommunityPool(ctx, amount, addr[0]) + require.Nil(t, err) + + require.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), distrKeeper.GetFeePool(ctx).CommunityPool) + require.Empty(t, bankKeeper.GetAllBalances(ctx, addr[0])) +} diff --git a/x/distribution/keeper/msg_server_test.go b/tests/integration/distribution/keeper/msg_server_test.go similarity index 100% rename from x/distribution/keeper/msg_server_test.go rename to tests/integration/distribution/keeper/msg_server_test.go diff --git a/tests/integration/distribution/keeper/params_test.go b/tests/integration/distribution/keeper/params_test.go new file mode 100644 index 000000000000..16ae36d69fee --- /dev/null +++ b/tests/integration/distribution/keeper/params_test.go @@ -0,0 +1,117 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +func (s *KeeperTestSuite) TestParams() { + // default params + communityTax := sdk.NewDecWithPrec(2, 2) // 2% + baseProposerReward := sdk.NewDecWithPrec(1, 2) // 1% + bonusProposerReward := sdk.NewDecWithPrec(4, 2) // 4% + withdrawAddrEnabled := true + + testCases := []struct { + name string + input types.Params + expErr bool + expErrMsg string + }{ + { + name: "community tax > 1", + input: types.Params{ + CommunityTax: sdk.NewDecWithPrec(2, 0), + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "community tax should be non-negative and less than one", + }, + { + name: "negative community tax", + input: types.Params{ + CommunityTax: sdk.NewDecWithPrec(-2, 1), + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "community tax should be non-negative and less than one", + }, + { + name: "base proposer reward > 1", + input: types.Params{ + CommunityTax: communityTax, + BaseProposerReward: sdk.NewDecWithPrec(2, 0), + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "sum of base, bonus proposer rewards, and community tax cannot be greater than one", + }, + { + name: "negative base proposer reward", + input: types.Params{ + CommunityTax: communityTax, + BaseProposerReward: sdk.NewDecWithPrec(-2, 0), + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "base proposer reward should be positive", + }, + { + name: "bonus proposer reward > 1", + input: types.Params{ + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: sdk.NewDecWithPrec(2, 0), + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "sum of base, bonus proposer rewards, and community tax cannot be greater than one", + }, + { + name: "negative bonus proposer reward", + input: types.Params{ + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: sdk.NewDecWithPrec(-2, 0), + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: true, + expErrMsg: "bonus proposer reward should be positive", + }, + { + name: "all good", + input: types.Params{ + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + }, + expErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + expected := s.distrKeeper.GetParams(s.ctx) + err := s.distrKeeper.SetParams(s.ctx, tc.input) + + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + expected = tc.input + s.Require().NoError(err) + } + + params := s.distrKeeper.GetParams(s.ctx) + s.Require().Equal(expected, params) + }) + } +} diff --git a/x/distribution/module_test.go b/tests/integration/distribution/module_test.go similarity index 100% rename from x/distribution/module_test.go rename to tests/integration/distribution/module_test.go diff --git a/x/distribution/keeper/allocation_test.go b/x/distribution/keeper/allocation_test.go index 0d47cc9e5e90..a747ce98ccb9 100644 --- a/x/distribution/keeper/allocation_test.go +++ b/x/distribution/keeper/allocation_test.go @@ -2,50 +2,53 @@ package keeper_test import ( "testing" + "time" "cosmossdk.io/math" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil" disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestAllocateTokensToValidatorWithCommission(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(sdk.ValAddress(addrs[0]), valConsPk1, sdk.NewInt(100), true) - val := stakingKeeper.Validator(ctx, valAddrs[0]) + val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val).AnyTimes() // allocate tokens tokens := sdk.DecCoins{ @@ -64,65 +67,70 @@ func TestAllocateTokensToValidatorWithCommission(t *testing.T) { } func TestAllocateTokensToManyValidators(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + feeCollectorAcc := authtypes.NewEmptyModuleAccount("fee_collector") + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), "fee_collector").Return(feeCollectorAcc) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - require.NoError(t, err) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - // reset fee pool + // reset fee pool & set params + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) - addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1234)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + valAddr0 := sdk.ValAddress(valConsAddr0) + val0, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) + require.NoError(t, err) + val0.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val0).AnyTimes() // create second validator with 0% commission - tstaking.Commission = stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[1], valConsPk2, sdk.NewInt(100), true) + valAddr1 := sdk.ValAddress(valConsAddr1) + val1, err := distrtestutil.CreateValidator(valConsPk1, math.NewInt(100)) + require.NoError(t, err) + val1.Commission = stakingtypes.NewCommission(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk1)).Return(val1).AnyTimes() abciValA := abci.Validator{ - Address: valConsPk1.Address(), + Address: valConsPk0.Address(), Power: 100, } abciValB := abci.Validator{ - Address: valConsPk2.Address(), + Address: valConsPk1.Address(), Power: 100, } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero()) require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) - require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) - require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) - feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) - require.NotNil(t, feeCollector) - - // fund fee collector - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) - - accountKeeper.SetAccount(ctx, feeCollector) + bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), "fee_collector", disttypes.ModuleName, fees) votes := []abci.VoteInfo{ { @@ -137,94 +145,102 @@ func TestAllocateTokensToManyValidators(t *testing.T) { distrKeeper.AllocateTokens(ctx, 200, votes) // 98 outstanding rewards (100 less 2 to community pool) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards) // 2 community pool coins require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, distrKeeper.GetFeePool(ctx).CommunityPool) // 50% commission for first proposer, (0.5 * 98%) * 100 / 2 = 23.25 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission) // zero commission for second proposer - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero()) // just staking.proportional for first proposer less commission = (0.5 * 98%) * 100 / 2 = 24.50 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards) // proposer reward + staking.proportional for second proposer = (0.5 * (98%)) * 100 = 49 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards) } func TestAllocateTokensTruncation(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + feeCollectorAcc := authtypes.NewEmptyModuleAccount("fee_collector") + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), "fee_collector").Return(feeCollectorAcc) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // reset fee pool distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) - - addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) // create validator with 10% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(110), true) + valAddr0 := sdk.ValAddress(valConsAddr0) + val0, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) + require.NoError(t, err) + val0.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val0).AnyTimes() // create second validator with 10% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[1], valConsPk2, sdk.NewInt(100), true) + valAddr1 := sdk.ValAddress(valConsAddr1) + val1, err := distrtestutil.CreateValidator(valConsPk1, math.NewInt(100)) + require.NoError(t, err) + val1.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk1)).Return(val1).AnyTimes() // create third validator with 10% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[2], valConsPk3, sdk.NewInt(100), true) + valAddr2 := sdk.ValAddress(valConsAddr2) + val2, err := stakingtypes.NewValidator(sdk.ValAddress(valConsAddr2), valConsPk1, stakingtypes.Description{}) + require.NoError(t, err) + val2.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) + stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk2)).Return(val2).AnyTimes() abciValA := abci.Validator{ - Address: valConsPk1.Address(), + Address: valConsPk0.Address(), Power: 11, } abciValB := abci.Validator{ - Address: valConsPk2.Address(), + Address: valConsPk1.Address(), Power: 10, } - abciValС := abci.Validator{ - Address: valConsPk3.Address(), + abciValC := abci.Validator{ + Address: valConsPk2.Address(), Power: 10, } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsZero()) require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) - require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) - require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0).Rewards.IsZero()) + require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840))) - - feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) - require.NotNil(t, feeCollector) - - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) - - accountKeeper.SetAccount(ctx, feeCollector) + bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees) + bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), "fee_collector", disttypes.ModuleName, fees) votes := []abci.VoteInfo{ { @@ -236,13 +252,13 @@ func TestAllocateTokensTruncation(t *testing.T) { SignedLastBlock: true, }, { - Validator: abciValС, + Validator: abciValC, SignedLastBlock: true, }, } distrKeeper.AllocateTokens(ctx, 31, votes) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsValid()) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsValid()) - require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[2]).Rewards.IsValid()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0).Rewards.IsValid()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1).Rewards.IsValid()) + require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr2).Rewards.IsValid()) } diff --git a/x/distribution/keeper/common_test.go b/x/distribution/keeper/common_test.go index fbcb9e55560a..642cbe45cf30 100644 --- a/x/distribution/keeper/common_test.go +++ b/x/distribution/keeper/common_test.go @@ -10,10 +10,11 @@ import ( var ( PKS = simtestutil.CreateTestPubKeys(5) - valConsPk1 = PKS[0] - valConsPk2 = PKS[1] - valConsPk3 = PKS[2] + valConsPk0 = PKS[0] + valConsPk1 = PKS[1] + valConsPk2 = PKS[2] + valConsAddr0 = sdk.ConsAddress(valConsPk0.Address()) valConsAddr1 = sdk.ConsAddress(valConsPk1.Address()) valConsAddr2 = sdk.ConsAddress(valConsPk2.Address()) diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 376b4991e14d..caba2afb5ff3 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -3,58 +3,66 @@ package keeper_test import ( "testing" - "cosmossdk.io/math" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/testutil" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestCalculateRewardsBasic(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, sdk.NewInt(1000)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + // delegation mock + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del) - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) - // end block to bond validator and start new block - staking.EndBlocker(ctx, stakingKeeper) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - tstaking.Ctx = ctx - - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // historical count should be 2 (once for validator init, once for delegation init) require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) @@ -86,44 +94,58 @@ func TestCalculateRewardsBasic(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestCalculateRewardsAfterSlash(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) valPower := int64(100) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) + stake := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) + val, err := distrtestutil.CreateValidator(valConsPk0, stake) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + + // set mock calls + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(4) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del) + + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // end period endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) @@ -136,17 +158,23 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { // start out block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) - - // retrieve validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) + // slash the validator by 50% (simulated with manual calls; we assume the validator is bonded) + slashedTokens := distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) + require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) @@ -161,44 +189,57 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { // commission should be the other half require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial.QuoRaw(2))}}, - distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestCalculateRewardsAfterManySlashes(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) valPower := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) + stake := sdk.TokensFromConsensusPower(valPower, sdk.DefaultPowerReduction) + val, err := distrtestutil.CreateValidator(valConsPk0, stake) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // delegation mocks + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(4) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del) + + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // end period endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) @@ -211,25 +252,40 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { // start out block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + // slash the validator by 50% (simulated with manual calls; we assume the validator is bonded) + slashedTokens := distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) + require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens) - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) + // expect a call for the next slash with the updated validator + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(1) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator by 50% again - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) - - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) + slashedTokens = distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower/2, + sdk.NewDecWithPrec(2, 1), + &val, + &distrKeeper, + ) + require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) @@ -248,58 +304,73 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { // commission should be the other half require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(initial)}}, - distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestCalculateRewardsMultiDelegator(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) + + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr0 := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) require.NoError(t, err) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(100000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + del0 := stakingtypes.NewDelegation(addr0, valAddr, val.DelegatorShares) - // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + // set mock calls + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(4) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr0, valAddr).Return(del0).Times(1) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr0, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // allocate some rewards initial := int64(20) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // second delegation - tstaking.Ctx = ctx - tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + addr1 := sdk.AccAddress(valConsAddr1) + _, del1, err := distrtestutil.Delegate(ctx, distrKeeper, addr1, &val, sdk.NewInt(100), nil) + require.NoError(t, err) - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr1, valAddr).Return(del1) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(1) - // end block - staking.EndBlocker(ctx, stakingKeeper) + // call necessary hooks to update a delegation + err = distrKeeper.Hooks().AfterDelegationModified(ctx, addr1, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -311,75 +382,70 @@ func TestCalculateRewardsMultiDelegator(t *testing.T) { endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del0, endingPeriod) - // rewards for del1 should be 3/4 initial + // rewards for del0 should be 3/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 3 / 4)}}, rewards) // calculate delegation rewards for del2 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del2 should be 1/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 1 / 4)}}, rewards) // commission should be equal to initial (50% twice) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestWithdrawDelegationRewardsBasic(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) + require.NoError(t, err) - balancePower := int64(1000) - balanceTokens := stakingKeeper.TokensFromConsensusPower(ctx, balancePower) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) - accountKeeper.SetModuleAccount(ctx, distrAcc) + // delegation mock + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(5) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del).Times(3) - // create validator with 50% commission - power := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - valTokens := tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, power, true) - - // assert correct initial balance - expTokens := balanceTokens.Sub(valTokens) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, expTokens)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) - - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - // allocate some rewards - initial := stakingKeeper.TokensFromConsensusPower(ctx, 10) + initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) @@ -387,67 +453,70 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) { // historical count should be 2 (initial + latest for delegation) require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - // withdraw rewards - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + // withdraw rewards (the bank keeper should be called with the right amount of tokens to transfer) + expRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expRewards) + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddr), valAddr) require.Nil(t, err) // historical count should still be 2 (added one record, cleared one) require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) - // assert correct balance - exp := balanceTokens.Sub(valTokens).Add(initial.QuoRaw(2)) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) - - // withdraw commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + // withdraw commission (the bank keeper should be called with the right amount of tokens to transfer) + expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission) + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr) require.Nil(t, err) - // assert correct balance - exp = balanceTokens.Sub(valTokens).Add(initial) - require.Equal(t, - sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), - ) } func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100)) + require.NoError(t, err) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // create validator with 50% commission - valPower := int64(100) - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) + // delegation mock + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(5) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // end period endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) @@ -461,18 +530,33 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 10)) + initial := sdk.NewDecFromInt(sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - // slash the validator by 50% - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + valPower := int64(100) + // slash the validator by 50% (simulated with manual calls; we assume the validator is bonded) + distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) // slash the validator by 50% again - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) - - // fetch the validator again - val = stakingKeeper.Validator(ctx, valAddrs[0]) + // stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) + distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower/2, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) @@ -490,61 +574,97 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) + valPower := int64(100) // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - valPower := int64(100) - tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, sdk.TokensFromConsensusPower(valPower, sdk.DefaultPowerReduction)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // validator and delegation mocks + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del) + + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(2) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // allocate some rewards - initial := sdk.NewDecFromInt(stakingKeeper.TokensFromConsensusPower(ctx, 30)) + initial := sdk.NewDecFromInt(sdk.TokensFromConsensusPower(30, sdk.DefaultPowerReduction)) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) + // update validator mock + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(1) + // second delegation - tstaking.DelegateWithPower(sdk.AccAddress(valAddrs[1]), valAddrs[0], 100) + _, del2, err := distrtestutil.Delegate( + ctx, + distrKeeper, + sdk.AccAddress(valConsAddr1), + &val, + sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), + nil, + ) + require.NoError(t, err) - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + // new delegation mock and update validator mock + stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(1) - // end block - staking.EndBlocker(ctx, stakingKeeper) + // call necessary hooks to update a delegation + err = distrKeeper.Hooks().AfterDelegationModified(ctx, sdk.AccAddress(valConsAddr1), valAddr) + require.NoError(t, err) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -554,17 +674,22 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { // slash the validator again ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - stakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + distrtestutil.SlashValidator( + ctx, + valConsAddr0, + ctx.BlockHeight(), + valPower, + sdk.NewDecWithPrec(5, 1), + &val, + &distrKeeper, + ) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - // end period endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) @@ -576,73 +701,85 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards) // commission should be equal to initial (twice 50% commission, unaffected by slashing) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) } func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - distrKeeper.DeleteAllValidatorHistoricalRewards(ctx) - - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - initial := int64(20) - - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) - accountKeeper.SetModuleAccount(ctx, distrAcc) - - tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) // create validator with 50% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, sdk.NewInt(100)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) + // validator and delegation mocks + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del).Times(5) + + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(2) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator and delegation - val := stakingKeeper.Validator(ctx, valAddrs[0]) - del1 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) - // allocate some rewards + initial := int64(20) + tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))} distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // historical count should be 2 (validator init, delegation init) require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // second delegation - tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + _, del2, err := distrtestutil.Delegate( + ctx, + distrKeeper, + sdk.AccAddress(valConsAddr1), + &val, + sdk.NewInt(100), + nil, + ) + require.NoError(t, err) - // historical count should be 3 (second delegation init) - require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) + // new delegation mock and update validator mock + stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2).Times(3) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(6) - // fetch updated validator - val = stakingKeeper.Validator(ctx, valAddrs[0]) - del2 := stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + // call necessary hooks to update a delegation + err = distrKeeper.Hooks().AfterDelegationModified(ctx, sdk.AccAddress(valConsAddr1), valAddr) + require.NoError(t, err) - // end block - staking.EndBlocker(ctx, stakingKeeper) + // historical count should be 3 (second delegation init) + require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -651,25 +788,31 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // first delegator withdraws - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + expRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*3/4))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expRewards) + _, err = distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr) require.NoError(t, err) // second delegator withdraws - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + expRewards = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*1/4))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, sdk.AccAddress(valConsAddr1), expRewards) + _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valConsAddr1), valAddr) require.NoError(t, err) // historical count should be 3 (validator init + two delegations) require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // validator withdraws commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission) + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr) require.NoError(t, err) // end period endingPeriod := distrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards for del1 should be zero require.True(t, rewards.IsZero()) @@ -681,7 +824,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { require.True(t, rewards.IsZero()) // commission should be zero - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission.IsZero()) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -690,14 +833,16 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // first delegator withdraws again - _, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + expCommission = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*1/4))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission) + _, err = distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr) require.NoError(t, err) // end period endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards for del1 should be zero require.True(t, rewards.IsZero()) @@ -709,7 +854,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) // commission should be half initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -718,14 +863,16 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // withdraw commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + expCommission = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial))} + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission) + _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr) require.NoError(t, err) // end period endingPeriod = distrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards for del1 should be 1/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards) @@ -737,58 +884,67 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards) // commission should be zero - require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission.IsZero()) } func Test100PercentCommissionReward(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) + // reset fee pool + distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) + distrKeeper.SetParams(ctx, disttypes.DefaultParams()) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + // create validator with 50% commission + valAddr := sdk.ValAddress(valConsAddr0) + addr := sdk.AccAddress(valAddr) + val, err := distrtestutil.CreateValidator(valConsPk0, sdk.NewInt(100)) + require.NoError(t, err) + val.Commission = stakingtypes.NewCommission(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) - tstaking := teststaking.NewHelper(t, ctx, stakingKeeper) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - initial := int64(20) + // validator and delegation mocks + del := stakingtypes.NewDelegation(addr, valAddr, val.DelegatorShares) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(3) + stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del).Times(3) - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000))))) - accountKeeper.SetModuleAccount(ctx, distrAcc) + // run the necessary hooks manually (given that we are not running an actual staking module) + err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr) + require.NoError(t, err) + stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val).Times(2) - tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyNewDec(initial))} + // next block + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // create validator with 100% commission - tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(10, 1), sdk.NewDecWithPrec(10, 1), math.LegacyNewDec(0)) - tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) - stakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + // allocate some rewards + initial := int64(20) + tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))} + distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - // end block to bond validator - staking.EndBlocker(ctx, stakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - // fetch validator - val := stakingKeeper.Validator(ctx, valAddrs[0]) - // allocate some rewards distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - // end block - staking.EndBlocker(ctx, stakingKeeper) - // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) @@ -801,7 +957,7 @@ func Test100PercentCommissionReward(t *testing.T) { // allocate some more rewards distrKeeper.AllocateTokensToValidator(ctx, val, tokens) - rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(addr), valAddr) require.NoError(t, err) denom, _ := sdk.GetBaseDenom() diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index 009bddd4a157..18d1870d0a45 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -2,114 +2,117 @@ package keeper_test import ( "testing" + "time" "cosmossdk.io/math" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/testutil" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/testutil" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil" "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) func TestSetWithdrawAddr(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + addrs := simtestutil.CreateIncrementalAccounts(2) + + delegatorAddr := addrs[0] + withdrawAddr := addrs[1] + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + bankKeeper.EXPECT().BlockedAddr(withdrawAddr).Return(false).AnyTimes() + bankKeeper.EXPECT().BlockedAddr(distrAcc.GetAddress()).Return(true).AnyTimes() + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - - params := distrKeeper.GetParams(ctx) + params := types.DefaultParams() params.WithdrawAddrEnabled = false require.NoError(t, distrKeeper.SetParams(ctx, params)) - err = distrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) + err := distrKeeper.SetWithdrawAddr(ctx, delegatorAddr, withdrawAddr) require.NotNil(t, err) params.WithdrawAddrEnabled = true require.NoError(t, distrKeeper.SetParams(ctx, params)) - err = distrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) + err = distrKeeper.SetWithdrawAddr(ctx, delegatorAddr, withdrawAddr) require.Nil(t, err) - require.Error(t, distrKeeper.SetWithdrawAddr(ctx, addr[0], distrAcc.GetAddress())) + require.Error(t, distrKeeper.SetWithdrawAddr(ctx, delegatorAddr, distrAcc.GetAddress())) } func TestWithdrawValidatorCommission(t *testing.T) { - var ( - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + addrs := simtestutil.CreateIncrementalAccounts(1) - app, err := simtestutil.Setup(testutil.AppConfig, - &accountKeeper, - &bankKeeper, - &distrKeeper, - &stakingKeeper, - ) - require.NoError(t, err) + valAddr := sdk.ValAddress(addrs[0]) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) valCommission := sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(5).Quo(math.LegacyNewDec(4))), sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(3).Quo(math.LegacyNewDec(2))), } - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - // set module account coins - distrAcc := distrKeeper.GetDistributionAccount(ctx) - coins := sdk.NewCoins(sdk.NewCoin("mytoken", sdk.NewInt(2)), sdk.NewCoin("stake", sdk.NewInt(2))) - require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, distrAcc.GetName(), coins)) - - accountKeeper.SetModuleAccount(ctx, distrAcc) - - // check initial balance - balance := bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) - expTokens := stakingKeeper.TokensFromConsensusPower(ctx, 1000) - expCoins := sdk.NewCoins(sdk.NewCoin("stake", expTokens)) - require.Equal(t, expCoins, balance) + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), + ) // set outstanding rewards - distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: valCommission}) // set commission - distrKeeper.SetValidatorAccumulatedCommission(ctx, valAddrs[0], types.ValidatorAccumulatedCommission{Commission: valCommission}) + distrKeeper.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: valCommission}) // withdraw commission - _, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) - require.NoError(t, err) + coins := sdk.NewCoins(sdk.NewCoin("mytoken", sdk.NewInt(1)), sdk.NewCoin("stake", sdk.NewInt(1))) + // if SendCoinsFromModuleToAccount is called, we know that the withdraw was successful + bankKeeper.EXPECT().SendCoinsFromModuleToAccount(gomock.Any(), "distribution", addrs[0], coins).Return(nil) - // check balance increase - balance = bankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) - require.Equal(t, sdk.NewCoins( - sdk.NewCoin("mytoken", sdk.NewInt(1)), - sdk.NewCoin("stake", expTokens.AddRaw(1)), - ), balance) + _, err := distrKeeper.WithdrawValidatorCommission(ctx, valAddr) + require.NoError(t, err) // check remainder - remainder := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission + remainder := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr).Commission require.Equal(t, sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(1).Quo(math.LegacyNewDec(4))), sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(2))), @@ -119,31 +122,39 @@ func TestWithdrawValidatorCommission(t *testing.T) { } func TestGetTotalRewards(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + addrs := simtestutil.CreateIncrementalAccounts(2) + + valAddr0 := sdk.ValAddress(addrs[0]) + valAddr1 := sdk.ValAddress(addrs[1]) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) valCommission := sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", math.LegacyNewDec(5).Quo(math.LegacyNewDec(4))), sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(3).Quo(math.LegacyNewDec(2))), } - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1000000000)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addr) - - distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) - distrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[1], types.ValidatorOutstandingRewards{Rewards: valCommission}) + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddr0, types.ValidatorOutstandingRewards{Rewards: valCommission}) + distrKeeper.SetValidatorOutstandingRewards(ctx, valAddr1, types.ValidatorOutstandingRewards{Rewards: valCommission}) expectedRewards := valCommission.MulDec(math.LegacyNewDec(2)) totalRewards := distrKeeper.GetTotalRewards(ctx) @@ -152,35 +163,39 @@ func TestGetTotalRewards(t *testing.T) { } func TestFundCommunityPool(t *testing.T) { - var ( - bankKeeper bankkeeper.Keeper - distrKeeper keeper.Keeper - stakingKeeper *stakingkeeper.Keeper - ) - - app, err := simtestutil.Setup(testutil.AppConfig, - &bankKeeper, - &distrKeeper, - &stakingKeeper, + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: time.Now()}) + addrs := simtestutil.CreateIncrementalAccounts(1) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) // reset fee pool distrKeeper.SetFeePool(ctx, types.InitialFeePool()) - addr := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, math.ZeroInt()) - - amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - require.NoError(t, banktestutil.FundAccount(bankKeeper, ctx, addr[0], amount)) - initPool := distrKeeper.GetFeePool(ctx) require.Empty(t, initPool.CommunityPool) - err = distrKeeper.FundCommunityPool(ctx, amount, addr[0]) + amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), addrs[0], "distribution", amount).Return(nil) + err := distrKeeper.FundCommunityPool(ctx, amount, addrs[0]) require.Nil(t, err) require.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), distrKeeper.GetFeePool(ctx).CommunityPool) - require.Empty(t, bankKeeper.GetAllBalances(ctx, addr[0])) } diff --git a/x/distribution/keeper/params_test.go b/x/distribution/keeper/params_test.go index 16ae36d69fee..fc6c7248a52d 100644 --- a/x/distribution/keeper/params_test.go +++ b/x/distribution/keeper/params_test.go @@ -1,11 +1,45 @@ package keeper_test import ( + "testing" + + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil" "github.com/cosmos/cosmos-sdk/x/distribution/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) -func (s *KeeperTestSuite) TestParams() { +func TestParams(t *testing.T) { + ctrl := gomock.NewController(t) + key := sdk.NewKVStoreKey(disttypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Height: 1}) + + bankKeeper := distrtestutil.NewMockBankKeeper(ctrl) + stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl) + accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl) + + accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress()) + + distrKeeper := keeper.NewKeeper( + encCfg.Codec, + key, + accountKeeper, + bankKeeper, + stakingKeeper, + "fee_collector", + authtypes.NewModuleAddress("gov").String(), + ) + // default params communityTax := sdk.NewDecWithPrec(2, 2) // 2% baseProposerReward := sdk.NewDecWithPrec(1, 2) // 1% @@ -98,20 +132,20 @@ func (s *KeeperTestSuite) TestParams() { for _, tc := range testCases { tc := tc - s.Run(tc.name, func() { - expected := s.distrKeeper.GetParams(s.ctx) - err := s.distrKeeper.SetParams(s.ctx, tc.input) + t.Run(tc.name, func(t *testing.T) { + expected := distrKeeper.GetParams(ctx) + err := distrKeeper.SetParams(ctx, tc.input) if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) } else { expected = tc.input - s.Require().NoError(err) + require.NoError(t, err) } - params := s.distrKeeper.GetParams(s.ctx) - s.Require().Equal(expected, params) + params := distrKeeper.GetParams(ctx) + require.Equal(t, expected, params) }) } } diff --git a/x/distribution/testutil/expected_keepers_mocks.go b/x/distribution/testutil/expected_keepers_mocks.go new file mode 100644 index 000000000000..585d2cb57792 --- /dev/null +++ b/x/distribution/testutil/expected_keepers_mocks.go @@ -0,0 +1,376 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/distribution/types/expected_keepers.go + +// Package testutil is a generated GoMock package. +package testutil + +import ( + reflect "reflect" + + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" + types1 "github.com/cosmos/cosmos-sdk/x/staking/types" + gomock "github.com/golang/mock/gomock" +) + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetAccount mocks base method. +func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, addr) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) +} + +// GetModuleAccount mocks base method. +func (m *MockAccountKeeper) GetModuleAccount(ctx types.Context, name string) types0.ModuleAccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAccount", ctx, name) + ret0, _ := ret[0].(types0.ModuleAccountI) + return ret0 +} + +// GetModuleAccount indicates an expected call of GetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAccount), ctx, name) +} + +// GetModuleAddress mocks base method. +func (m *MockAccountKeeper) GetModuleAddress(name string) types.AccAddress { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetModuleAddress", name) + ret0, _ := ret[0].(types.AccAddress) + return ret0 +} + +// GetModuleAddress indicates an expected call of GetModuleAddress. +func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetModuleAddress", reflect.TypeOf((*MockAccountKeeper)(nil).GetModuleAddress), name) +} + +// SetModuleAccount mocks base method. +func (m *MockAccountKeeper) SetModuleAccount(arg0 types.Context, arg1 types0.ModuleAccountI) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetModuleAccount", arg0, arg1) +} + +// SetModuleAccount indicates an expected call of SetModuleAccount. +func (mr *MockAccountKeeperMockRecorder) SetModuleAccount(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetModuleAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetModuleAccount), arg0, arg1) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// BlockedAddr mocks base method. +func (m *MockBankKeeper) BlockedAddr(addr types.AccAddress) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockedAddr", addr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// BlockedAddr indicates an expected call of BlockedAddr. +func (mr *MockBankKeeperMockRecorder) BlockedAddr(addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockedAddr", reflect.TypeOf((*MockBankKeeper)(nil).BlockedAddr), addr) +} + +// GetAllBalances mocks base method. +func (m *MockBankKeeper) GetAllBalances(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// GetAllBalances indicates an expected call of GetAllBalances. +func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllBalances", reflect.TypeOf((*MockBankKeeper)(nil).GetAllBalances), ctx, addr) +} + +// SendCoinsFromAccountToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt) +} + +// SendCoinsFromModuleToAccount mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt) +} + +// SendCoinsFromModuleToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx types.Context, senderModule, recipientModule string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", ctx, senderModule, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToModule indicates an expected call of SendCoinsFromModuleToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderModule, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToModule), ctx, senderModule, recipientModule, amt) +} + +// SpendableCoins mocks base method. +func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// SpendableCoins indicates an expected call of SpendableCoins. +func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) +} + +// MockStakingKeeper is a mock of StakingKeeper interface. +type MockStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockStakingKeeperMockRecorder +} + +// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper. +type MockStakingKeeperMockRecorder struct { + mock *MockStakingKeeper +} + +// NewMockStakingKeeper creates a new mock instance. +func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper { + mock := &MockStakingKeeper{ctrl: ctrl} + mock.recorder = &MockStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { + return m.recorder +} + +// Delegation mocks base method. +func (m *MockStakingKeeper) Delegation(arg0 types.Context, arg1 types.AccAddress, arg2 types.ValAddress) types1.DelegationI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delegation", arg0, arg1, arg2) + ret0, _ := ret[0].(types1.DelegationI) + return ret0 +} + +// Delegation indicates an expected call of Delegation. +func (mr *MockStakingKeeperMockRecorder) Delegation(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delegation", reflect.TypeOf((*MockStakingKeeper)(nil).Delegation), arg0, arg1, arg2) +} + +// GetAllDelegatorDelegations mocks base method. +func (m *MockStakingKeeper) GetAllDelegatorDelegations(ctx types.Context, delegator types.AccAddress) []types1.Delegation { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllDelegatorDelegations", ctx, delegator) + ret0, _ := ret[0].([]types1.Delegation) + return ret0 +} + +// GetAllDelegatorDelegations indicates an expected call of GetAllDelegatorDelegations. +func (mr *MockStakingKeeperMockRecorder) GetAllDelegatorDelegations(ctx, delegator interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllDelegatorDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).GetAllDelegatorDelegations), ctx, delegator) +} + +// GetAllSDKDelegations mocks base method. +func (m *MockStakingKeeper) GetAllSDKDelegations(ctx types.Context) []types1.Delegation { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllSDKDelegations", ctx) + ret0, _ := ret[0].([]types1.Delegation) + return ret0 +} + +// GetAllSDKDelegations indicates an expected call of GetAllSDKDelegations. +func (mr *MockStakingKeeperMockRecorder) GetAllSDKDelegations(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllSDKDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).GetAllSDKDelegations), ctx) +} + +// GetAllValidators mocks base method. +func (m *MockStakingKeeper) GetAllValidators(ctx types.Context) []types1.Validator { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllValidators", ctx) + ret0, _ := ret[0].([]types1.Validator) + return ret0 +} + +// GetAllValidators indicates an expected call of GetAllValidators. +func (mr *MockStakingKeeperMockRecorder) GetAllValidators(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetAllValidators), ctx) +} + +// IterateDelegations mocks base method. +func (m *MockStakingKeeper) IterateDelegations(ctx types.Context, delegator types.AccAddress, fn func(int64, types1.DelegationI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateDelegations", ctx, delegator, fn) +} + +// IterateDelegations indicates an expected call of IterateDelegations. +func (mr *MockStakingKeeperMockRecorder) IterateDelegations(ctx, delegator, fn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateDelegations", reflect.TypeOf((*MockStakingKeeper)(nil).IterateDelegations), ctx, delegator, fn) +} + +// IterateValidators mocks base method. +func (m *MockStakingKeeper) IterateValidators(arg0 types.Context, arg1 func(int64, types1.ValidatorI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateValidators", arg0, arg1) +} + +// IterateValidators indicates an expected call of IterateValidators. +func (mr *MockStakingKeeperMockRecorder) IterateValidators(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateValidators", reflect.TypeOf((*MockStakingKeeper)(nil).IterateValidators), arg0, arg1) +} + +// Validator mocks base method. +func (m *MockStakingKeeper) Validator(arg0 types.Context, arg1 types.ValAddress) types1.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Validator", arg0, arg1) + ret0, _ := ret[0].(types1.ValidatorI) + return ret0 +} + +// Validator indicates an expected call of Validator. +func (mr *MockStakingKeeperMockRecorder) Validator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockStakingKeeper)(nil).Validator), arg0, arg1) +} + +// ValidatorByConsAddr mocks base method. +func (m *MockStakingKeeper) ValidatorByConsAddr(arg0 types.Context, arg1 types.ConsAddress) types1.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatorByConsAddr", arg0, arg1) + ret0, _ := ret[0].(types1.ValidatorI) + return ret0 +} + +// ValidatorByConsAddr indicates an expected call of ValidatorByConsAddr. +func (mr *MockStakingKeeperMockRecorder) ValidatorByConsAddr(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorByConsAddr), arg0, arg1) +} + +// MockStakingHooks is a mock of StakingHooks interface. +type MockStakingHooks struct { + ctrl *gomock.Controller + recorder *MockStakingHooksMockRecorder +} + +// MockStakingHooksMockRecorder is the mock recorder for MockStakingHooks. +type MockStakingHooksMockRecorder struct { + mock *MockStakingHooks +} + +// NewMockStakingHooks creates a new mock instance. +func NewMockStakingHooks(ctrl *gomock.Controller) *MockStakingHooks { + mock := &MockStakingHooks{ctrl: ctrl} + mock.recorder = &MockStakingHooksMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingHooks) EXPECT() *MockStakingHooksMockRecorder { + return m.recorder +} + +// AfterDelegationModified mocks base method. +func (m *MockStakingHooks) AfterDelegationModified(ctx types.Context, delAddr types.AccAddress, valAddr types.ValAddress) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AfterDelegationModified", ctx, delAddr, valAddr) +} + +// AfterDelegationModified indicates an expected call of AfterDelegationModified. +func (mr *MockStakingHooksMockRecorder) AfterDelegationModified(ctx, delAddr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterDelegationModified", reflect.TypeOf((*MockStakingHooks)(nil).AfterDelegationModified), ctx, delAddr, valAddr) +} + +// AfterValidatorCreated mocks base method. +func (m *MockStakingHooks) AfterValidatorCreated(ctx types.Context, valAddr types.ValAddress) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AfterValidatorCreated", ctx, valAddr) +} + +// AfterValidatorCreated indicates an expected call of AfterValidatorCreated. +func (mr *MockStakingHooksMockRecorder) AfterValidatorCreated(ctx, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterValidatorCreated", reflect.TypeOf((*MockStakingHooks)(nil).AfterValidatorCreated), ctx, valAddr) +} diff --git a/x/distribution/testutil/staking_helper.go b/x/distribution/testutil/staking_helper.go new file mode 100644 index 000000000000..925bd8bad875 --- /dev/null +++ b/x/distribution/testutil/staking_helper.go @@ -0,0 +1,128 @@ +package testutil + +import ( + "fmt" + + "cosmossdk.io/math" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func CreateValidator(pk cryptotypes.PubKey, stake math.Int) (stakingtypes.Validator, error) { + valConsAddr := sdk.GetConsAddress(pk) + val, err := stakingtypes.NewValidator(sdk.ValAddress(valConsAddr), pk, stakingtypes.Description{}) + val.Tokens = stake + val.DelegatorShares = math.LegacyNewDecFromInt(val.Tokens) + return val, err +} + +func CallCreateValidatorHooks(ctx sdk.Context, k keeper.Keeper, addr sdk.AccAddress, valAddr sdk.ValAddress) error { + err := k.Hooks().AfterValidatorCreated(ctx, valAddr) + if err != nil { + return err + } + + err = k.Hooks().BeforeDelegationCreated(ctx, addr, valAddr) + if err != nil { + return err + } + + err = k.Hooks().AfterDelegationModified(ctx, addr, valAddr) + if err != nil { + return err + } + + return nil +} + +// SlashValidator copies what x/staking Slash does. It should be used for testing only. +// And it must be updated whenever the original function is updated. +// The passed validator will get its tokens updated. +func SlashValidator( + ctx sdk.Context, + consAddr sdk.ConsAddress, + infractionHeight int64, + power int64, + slashFactor sdk.Dec, + validator *stakingtypes.Validator, + distrKeeper *keeper.Keeper, +) math.Int { + if slashFactor.IsNegative() { + panic(fmt.Errorf("attempted to slash with a negative slash factor: %v", slashFactor)) + } + + // call the before-modification hook + err := distrKeeper.Hooks().BeforeValidatorModified(ctx, validator.GetOperator()) + if err != nil { + panic(err) + } + + // we simplify this part, as we won't be able to test redelegations or + // unbonding delegations + if infractionHeight != ctx.BlockHeight() { + // if a new test lands here we might need to update this function to handle redelegations and unbonding + // or just make it an integration test. + panic("we can't test any other case here") + } + + slashAmountDec := sdk.NewDecFromInt(validator.Tokens).Mul(sdk.NewDecWithPrec(5, 1)) + slashAmount := slashAmountDec.TruncateInt() + + // cannot decrease balance below zero + tokensToBurn := sdk.MinInt(slashAmount, validator.Tokens) + tokensToBurn = sdk.MaxInt(tokensToBurn, math.ZeroInt()) // defensive. + + // we need to calculate the *effective* slash fraction for distribution + if validator.Tokens.IsPositive() { + effectiveFraction := sdk.NewDecFromInt(tokensToBurn).QuoRoundUp(sdk.NewDecFromInt(validator.Tokens)) + // possible if power has changed + if effectiveFraction.GT(math.LegacyOneDec()) { + effectiveFraction = math.LegacyOneDec() + } + // call the before-slashed hook + distrKeeper.Hooks().BeforeValidatorSlashed(ctx, validator.GetOperator(), effectiveFraction) + } + // Deduct from validator's bonded tokens and update the validator. + // Burn the slashed tokens from the pool account and decrease the total supply. + validator.Tokens = validator.Tokens.Sub(tokensToBurn) + + return tokensToBurn +} + +// Delegate imitate what x/staking Delegate does. It should be used for testing only. +// If a delegation is passed we are simulating an update to a previous delegation, +// if it's nil then we simulate a new delegation. +func Delegate( + ctx sdk.Context, + distrKeeper keeper.Keeper, + delegator sdk.AccAddress, + validator *stakingtypes.Validator, + amount math.Int, + delegation *stakingtypes.Delegation, +) ( + newShares sdk.Dec, + updatedDel stakingtypes.Delegation, + err error, +) { + if delegation != nil { + err = distrKeeper.Hooks().BeforeDelegationSharesModified(ctx, delegator, validator.GetOperator()) + } else { + err = distrKeeper.Hooks().BeforeDelegationCreated(ctx, delegator, validator.GetOperator()) + del := stakingtypes.NewDelegation(delegator, validator.GetOperator(), math.LegacyZeroDec()) + delegation = &del + } + + if err != nil { + return math.LegacyZeroDec(), stakingtypes.Delegation{}, err + } + + // Add tokens from delegation to validator + updateVal, newShares := validator.AddTokensFromDel(amount) + *validator = updateVal + + delegation.Shares = delegation.Shares.Add(newShares) + + return newShares, *delegation, nil +} diff --git a/x/distribution/simulation/decoder_test.go b/x/simulation/decoder_test.go similarity index 100% rename from x/distribution/simulation/decoder_test.go rename to x/simulation/decoder_test.go diff --git a/x/distribution/simulation/operations_test.go b/x/simulation/operations_test.go similarity index 95% rename from x/distribution/simulation/operations_test.go rename to x/simulation/operations_test.go index 34eebb0be28c..a5177b0123c3 100644 --- a/x/distribution/simulation/operations_test.go +++ b/x/simulation/operations_test.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/configurator" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" @@ -20,7 +21,6 @@ import ( banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/simulation" - "github.com/cosmos/cosmos-sdk/x/distribution/testutil" "github.com/cosmos/cosmos-sdk/x/distribution/types" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" @@ -103,7 +103,7 @@ func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() { validator0 := suite.getTestingValidator0(accounts) // setup delegation - delTokens := suite.stakingKeeper.TokensFromConsensusPower(suite.ctx, 2) + delTokens := sdk.TokensFromConsensusPower(2, sdk.DefaultPowerReduction) validator0, issuedShares := validator0.AddTokensFromDel(delTokens) delegator := accounts[1] delegation := stakingtypes.NewDelegation(delegator.Address, validator0.GetOperator(), issuedShares) @@ -235,18 +235,29 @@ type SimTestSuite struct { } func (suite *SimTestSuite) SetupTest() { - app, err := simtestutil.Setup(testutil.AppConfig, + var ( + appBuilder *runtime.AppBuilder + err error + ) + suite.app, err = simtestutil.Setup(configurator.NewAppConfig( + configurator.AuthModule(), + configurator.ParamsModule(), + configurator.BankModule(), + configurator.StakingModule(), + configurator.TxModule(), + configurator.DistributionModule(), + ), &suite.accountKeeper, + &suite.bankKeeper, &suite.cdc, - &suite.txConfig, + &appBuilder, &suite.stakingKeeper, - &suite.accountKeeper, - &suite.bankKeeper, &suite.distrKeeper, + &suite.txConfig, ) + suite.NoError(err) - suite.app = app - suite.ctx = app.BaseApp.NewContext(false, tmproto.Header{}) + suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{}) genesisVals := suite.stakingKeeper.GetAllValidators(suite.ctx) suite.Require().Len(genesisVals, 1) diff --git a/x/staking/simulation/operations_test.go b/x/staking/simulation/operations_test.go index 1258251fe40b..bc241655f022 100644 --- a/x/staking/simulation/operations_test.go +++ b/x/staking/simulation/operations_test.go @@ -381,7 +381,7 @@ func setupValidatorRewards(app *simapp.SimApp, ctx sdk.Context, valAddress sdk.V decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyOneDec())} historicalRewards := distrtypes.NewValidatorHistoricalRewards(decCoins, 2) app.DistrKeeper.SetValidatorHistoricalRewards(ctx, valAddress, 2, historicalRewards) - // setup current revards + // setup current rewards currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3) app.DistrKeeper.SetValidatorCurrentRewards(ctx, valAddress, currentRewards) }