mukan-sdk/x/distribution/keeper/delegation_test.go
Mukan Erkin Törük 20afb5db80
Some checks failed
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
Build & Push SDK Proto Builder / build (push) Has been cancelled
initial: sovereign Mukan Network fork
2026-05-11 03:18:24 +03:00

1051 lines
41 KiB
Go

package keeper_test
import (
"testing"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/runtime"
"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"
disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
func TestCalculateRewardsBasic(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// create validator with 50% commission
valAddr := sdk.ValAddress(valConsAddr0)
addr := sdk.AccAddress(valAddr)
val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(1000))
require.NoError(t, err)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// delegation mock
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
// 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)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// 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, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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)}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// end period
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
}
func TestCalculateRewardsAfterSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// create validator with 50% commission
valAddr := sdk.ValAddress(valConsAddr0)
addr := sdk.AccAddress(valAddr)
valPower := int64(100)
stake := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction)
val, err := distrtestutil.CreateValidator(valConsPk0, stake)
require.NoError(t, err)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
// set mock calls
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
// 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)
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
// start out block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// slash the validator by 50% (simulated with manual calls; we assume the validator is bonded)
slashedTokens := distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
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 := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// end period
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be half the tokens
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}}, rewards)
// commission should be the other half
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}},
valCommission.Commission)
}
func TestCalculateRewardsAfterManySlashes(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// create validator with 50% commission
valAddr := sdk.ValAddress(valConsAddr0)
addr := sdk.AccAddress(valAddr)
valPower := int64(100)
stake := sdk.TokensFromConsensusPower(valPower, sdk.DefaultPowerReduction)
val, err := distrtestutil.CreateValidator(valConsPk0, stake)
require.NoError(t, err)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// delegation mocks
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
// 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)
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
// start out block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// slash the validator by 50% (simulated with manual calls; we assume the validator is bonded)
slashedTokens := distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens)
// expect a call for the next slash with the updated validator
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
// increase block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// allocate some rewards
initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// slash the validator by 50% again
slashedTokens = distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower/2,
math.LegacyNewDecWithPrec(2, 1),
&val,
&distrKeeper,
stakingKeeper,
)
require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens)
// increase block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// end period
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be half the tokens
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}, rewards)
// commission should be the other half
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}},
valCommission.Commission)
}
func TestCalculateRewardsMultiDelegator(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(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)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
del0 := stakingtypes.NewDelegation(addr0.String(), valAddr.String(), val.DelegatorShares)
// set mock calls
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr0, valAddr).Return(del0, nil).Times(1)
// 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)
// allocate some rewards
initial := int64(20)
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// second delegation
addr1 := sdk.AccAddress(valConsAddr1)
_, del1, err := distrtestutil.Delegate(ctx, distrKeeper, addr1, &val, math.NewInt(100), nil, stakingKeeper)
require.NoError(t, err)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr1, valAddr).Return(del1, nil)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
// 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)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del0, endingPeriod)
require.NoError(t, err)
// 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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod)
require.NoError(t, err)
// 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)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, valCommission.Commission)
}
func TestWithdrawDelegationRewardsBasic(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// 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)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// delegation mock
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(5)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).Times(3)
// 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)
// allocate some rewards
initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// historical count should be 2 (initial + latest for delegation)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
// 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))
// 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)
}
func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// 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)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// delegation mock
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(5)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
// 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)
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be zero
require.True(t, rewards.IsZero())
// start out block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// allocate some rewards
initial := math.LegacyNewDecFromInt(sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction))
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
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,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
// slash the validator by 50% again
// stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, math.LegacyNewDecWithPrec(5, 1))
distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower/2,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
// increase block height
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// end period
endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards should be half the tokens
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards)
// commission should be the other half
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
}
func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
valPower := int64(100)
// create validator with 50% commission
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(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// validator and delegation mocks
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
// 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, nil).Times(2)
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some rewards
initial := math.LegacyNewDecFromInt(sdk.TokensFromConsensusPower(30, sdk.DefaultPowerReduction))
tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// slash the validator
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// update validator mock
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
// second delegation
_, del2, err := distrtestutil.Delegate(
ctx,
distrKeeper,
sdk.AccAddress(valConsAddr1),
&val,
sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction),
nil,
stakingKeeper,
)
require.NoError(t, err)
// new delegation mock and update validator mock
stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2, nil)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
// 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)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// slash the validator again
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
distrtestutil.SlashValidator(
ctx,
valConsAddr0,
ctx.BlockHeight(),
valPower,
math.LegacyNewDecWithPrec(5, 1),
&val,
&distrKeeper,
stakingKeeper,
)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// calculate delegation rewards for del1
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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)
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
}
func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// 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)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
// validator and delegation mocks
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).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, nil).Times(2)
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some rewards
initial := int64(20)
tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// historical count should be 2 (validator init, delegation init)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
// second delegation
_, del2, err := distrtestutil.Delegate(
ctx,
distrKeeper,
sdk.AccAddress(valConsAddr1),
&val,
math.NewInt(100),
nil,
stakingKeeper,
)
require.NoError(t, err)
// new delegation mock and update validator mock
stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2, nil).Times(3)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(6)
// call necessary hooks to update a delegation
err = distrKeeper.Hooks().AfterDelegationModified(ctx, sdk.AccAddress(valConsAddr1), valAddr)
require.NoError(t, err)
// historical count should be 3 (second delegation init)
require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// first delegator withdraws
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
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
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, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards for del1 should be zero
require.True(t, rewards.IsZero())
// calculate delegation rewards for del2
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// rewards for del2 should be zero
require.True(t, rewards.IsZero())
// commission should be zero
valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.True(t, valCommission.Commission.IsZero())
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// first delegator withdraws again
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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// rewards for del1 should be zero
require.True(t, rewards.IsZero())
// calculate delegation rewards for del2
rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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
valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// withdraw commission
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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
require.NoError(t, err)
// 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, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
require.NoError(t, err)
// 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
valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
require.NoError(t, err)
require.True(t, valCommission.Commission.IsZero())
}
func Test100PercentCommissionReward(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
storeService := runtime.NewKVStoreService(key)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
distrKeeper := keeper.NewKeeper(
encCfg.Codec,
storeService,
accountKeeper,
bankKeeper,
stakingKeeper,
"fee_collector",
authtypes.NewModuleAddress("gov").String(),
)
// reset fee pool
require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
// 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)
val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(10, 1), math.LegacyNewDecWithPrec(10, 1), math.LegacyNewDec(0))
// validator and delegation mocks
del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).Times(3)
// 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, nil).Times(2)
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some rewards
initial := int64(20)
tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// allocate some more rewards
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr)
require.NoError(t, err)
zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())}
require.True(t, rewards.Equal(zeroRewards))
events := ctx.EventManager().Events()
lastEvent := events[len(events)-1]
var hasValue bool
for _, attr := range lastEvent.Attributes {
if attr.Key == "amount" && attr.Value == "0stake" {
hasValue = true
}
}
require.True(t, hasValue)
}