package v2_test import ( "fmt" "sync/atomic" "testing" "time" cmtproto "git.cw.tr/mukan-network/mukan-consensus/proto/tendermint/types" "github.com/stretchr/testify/require" "cosmossdk.io/depinject" "cosmossdk.io/log" sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "git.cw.tr/mukan-network/mukan-sdk/runtime" "git.cw.tr/mukan-network/mukan-sdk/testutil" simtestutil "git.cw.tr/mukan-network/mukan-sdk/testutil/sims" sdk "git.cw.tr/mukan-network/mukan-sdk/types" moduletestutil "git.cw.tr/mukan-network/mukan-sdk/types/module/testutil" "git.cw.tr/mukan-network/mukan-sdk/x/auth" authexported "git.cw.tr/mukan-network/mukan-sdk/x/auth/exported" "git.cw.tr/mukan-network/mukan-sdk/x/auth/keeper" v1 "git.cw.tr/mukan-network/mukan-sdk/x/auth/migrations/v1" v4 "git.cw.tr/mukan-network/mukan-sdk/x/auth/migrations/v4" authtestutil "git.cw.tr/mukan-network/mukan-sdk/x/auth/testutil" authtypes "git.cw.tr/mukan-network/mukan-sdk/x/auth/types" "git.cw.tr/mukan-network/mukan-sdk/x/auth/vesting/exported" "git.cw.tr/mukan-network/mukan-sdk/x/auth/vesting/types" bankkeeper "git.cw.tr/mukan-network/mukan-sdk/x/bank/keeper" stakingkeeper "git.cw.tr/mukan-network/mukan-sdk/x/staking/keeper" stakingtypes "git.cw.tr/mukan-network/mukan-sdk/x/staking/types" ) type mockSubspace struct { ps authtypes.Params } func newMockSubspace(ps authtypes.Params) mockSubspace { return mockSubspace{ps: ps} } func (ms mockSubspace) GetParamSet(ctx sdk.Context, ps authexported.ParamSet) { *ps.(*authtypes.Params) = ms.ps } func TestMigrateVestingAccounts(t *testing.T) { encCfg := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}) cdc := encCfg.Codec storeKey := storetypes.NewKVStoreKey(v1.ModuleName) tKey := storetypes.NewTransientStoreKey("transient_test") ctx := testutil.DefaultContext(storeKey, tKey) storeService := runtime.NewKVStoreService(storeKey) var ( accountKeeper keeper.AccountKeeper bankKeeper bankkeeper.Keeper stakingKeeper *stakingkeeper.Keeper ) app, err := simtestutil.Setup( depinject.Configs( authtestutil.AppConfig, depinject.Supply(log.NewNopLogger()), ), &accountKeeper, &bankKeeper, &stakingKeeper, ) require.NoError(t, err) legacySubspace := newMockSubspace(authtypes.DefaultParams()) require.NoError(t, v4.Migrate(ctx, storeService, legacySubspace, cdc)) ctx = app.NewContextLegacy(false, cmtproto.Header{Time: time.Now()}) require.NoError(t, stakingKeeper.SetParams(ctx, stakingtypes.DefaultParams())) lastAccNum := uint64(1000) createBaseAccount := func(addr sdk.AccAddress) *authtypes.BaseAccount { baseAccount := authtypes.NewBaseAccountWithAddress(addr) require.NoError(t, baseAccount.SetAccountNumber(atomic.AddUint64(&lastAccNum, 1))) return baseAccount } testCases := []struct { name string prepareFunc func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) garbageFunc func(ctx sdk.Context, vesting exported.VestingAccount, accounKeeper keeper.AccountKeeper) error tokenAmount int64 expVested int64 expFree int64 blockTime int64 }{ { "delayed vesting has vested, multiple delegations less than the total account balance", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(200))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix()) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) err = accountKeeper.Params.Set(ctx, authtypes.DefaultParams()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 0, 300, 0, }, { "delayed vesting has vested, single delegations which exceed the vested amount", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) baseAccount := createBaseAccount(delegatorAddr) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(200))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix()) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 0, 300, 0, }, { "delayed vesting has vested, multiple delegations which exceed the vested amount", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(200))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix()) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 0, 300, 0, }, { "delayed vesting has not vested, single delegations which exceed the vested amount", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(200))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 200, 100, 0, }, { "delayed vesting has not vested, multiple delegations which exceed the vested amount", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(200))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 200, 100, 0, }, { "not end time", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(100), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 300, 0, 0, }, { "delayed vesting has not vested, single delegation greater than the total account balance", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 300, 0, 0, }, { "delayed vesting has vested, single delegation greater than the total account balance", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix()) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 0, 300, 0, }, { "continuous vesting, start time after blocktime", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { startTime := ctx.BlockTime().AddDate(1, 0, 0).Unix() endTime := ctx.BlockTime().AddDate(2, 0, 0).Unix() baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 300, 0, 0, }, { "continuous vesting, start time passed but not ended", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { startTime := ctx.BlockTime().AddDate(-1, 0, 0).Unix() endTime := ctx.BlockTime().AddDate(2, 0, 0).Unix() baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 200, 100, 0, }, { "continuous vesting, start time and endtime passed", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { startTime := ctx.BlockTime().AddDate(-2, 0, 0).Unix() endTime := ctx.BlockTime().AddDate(-1, 0, 0).Unix() baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) accountKeeper.SetAccount(ctx, delayedAccount) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 0, 300, 0, }, { "periodic vesting account, yet to be vested, some rewards delegated", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(100))) start := ctx.BlockTime().Unix() + int64(time.Hour/time.Second) periods := []types.Period{ { Length: int64((24 * time.Hour) / time.Second), Amount: vestedCoins, }, } account, err := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, start, periods) require.NoError(t, err) accountKeeper.SetAccount(ctx, account) _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(150), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 300, 100, 50, 0, }, { "periodic vesting account, nothing has vested yet", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { /* Test case: - periodic vesting account starts at time 1601042400 - account balance and original vesting: 3666666670000 - nothing has vested, we put the block time slightly after start time - expected vested: original vesting amount - expected free: zero - we're delegating the full original vesting */ startTime := int64(1601042400) baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(3666666670000))) periods := []types.Period{ { Length: 31536000, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(1833333335000))), }, { Length: 15638400, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, { Length: 15897600, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, } delayedAccount, err := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) // delegation of the original vesting _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(3666666670000), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 3666666670000, 3666666670000, 0, 1601042400 + 1, }, { "periodic vesting account, all has vested", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { /* Test case: - periodic vesting account starts at time 1601042400 - account balance and original vesting: 3666666670000 - all has vested, so we set the block time at initial time + sum of all periods times + 1 => 1601042400 + 31536000 + 15897600 + 15897600 + 1 - expected vested: zero - expected free: original vesting amount - we're delegating the full original vesting */ startTime := int64(1601042400) baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(3666666670000))) periods := []types.Period{ { Length: 31536000, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(1833333335000))), }, { Length: 15638400, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, { Length: 15897600, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, } delayedAccount, err := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods) require.NoError(t, err) ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+15897600+15897600+1, 0)) accountKeeper.SetAccount(ctx, delayedAccount) // delegation of the original vesting _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(3666666670000), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 3666666670000, 0, 3666666670000, 1601042400 + 31536000 + 15897600 + 15897600 + 1, }, { "periodic vesting account, first period has vested", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { /* Test case: - periodic vesting account starts at time 1601042400 - account balance and original vesting: 3666666670000 - first period have vested, so we set the block time at initial time + time of the first periods + 1 => 1601042400 + 31536000 + 1 - expected vested: original vesting - first period amount - expected free: first period amount - we're delegating the full original vesting */ startTime := int64(1601042400) baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(3666666670000))) periods := []types.Period{ { Length: 31536000, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(1833333335000))), }, { Length: 15638400, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, { Length: 15897600, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, } delayedAccount, err := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods) require.NoError(t, err) ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+1, 0)) accountKeeper.SetAccount(ctx, delayedAccount) // delegation of the original vesting _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(3666666670000), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 3666666670000, 3666666670000 - 1833333335000, 1833333335000, 1601042400 + 31536000 + 1, }, { "periodic vesting account, first 2 period has vested", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { /* Test case: - periodic vesting account starts at time 1601042400 - account balance and original vesting: 3666666670000 - first 2 periods have vested, so we set the block time at initial time + time of the two periods + 1 => 1601042400 + 31536000 + 15638400 + 1 - expected vested: original vesting - (sum of the first two periods amounts) - expected free: sum of the first two periods - we're delegating the full original vesting */ startTime := int64(1601042400) baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(3666666670000))) periods := []types.Period{ { Length: 31536000, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(1833333335000))), }, { Length: 15638400, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, { Length: 15897600, Amount: sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(916666667500))), }, } delayedAccount, err := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods) require.NoError(t, err) ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+15638400+1, 0)) accountKeeper.SetAccount(ctx, delayedAccount) // delegation of the original vesting _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(3666666670000), stakingtypes.Unbonded, validator, true) require.NoError(t, err) }, cleartTrackingFields, 3666666670000, 3666666670000 - 1833333335000 - 916666667500, 1833333335000 + 916666667500, 1601042400 + 31536000 + 15638400 + 1, }, { "vesting account has unbonding delegations in place", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) // delegation of the original vesting _, err = stakingKeeper.Delegate(ctx, delegatorAddr, sdkmath.NewInt(300), stakingtypes.Unbonded, validator, true) require.NoError(t, err) ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0)) valAddr, err := sdk.ValAddressFromBech32(validator.OperatorAddress) require.NoError(t, err) // un-delegation of the original vesting _, _, err = stakingKeeper.Undelegate(ctx, delegatorAddr, valAddr, sdkmath.LegacyNewDecFromInt(sdkmath.NewInt(300))) require.NoError(t, err) }, cleartTrackingFields, 450, 300, 0, 0, }, { "vesting account has never delegated anything", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) }, cleartTrackingFields, 450, 0, 0, 0, }, { "vesting account has no delegation but dirty DelegatedFree and DelegatedVesting fields", func(ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) { baseAccount := createBaseAccount(delegatorAddr) bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) vestedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(300))) delayedAccount, err := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix()) require.NoError(t, err) accountKeeper.SetAccount(ctx, delayedAccount) }, dirtyTrackingFields, 450, 0, 0, 0, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := accountKeeper.Params.Set(ctx, authtypes.DefaultParams()) require.NoError(t, err) addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, sdkmath.NewInt(tc.tokenAmount)) delegatorAddr := addrs[0] _, valAddr := createValidator(t, ctx, bankKeeper, stakingKeeper, tc.tokenAmount*2) validator, err := stakingKeeper.GetValidator(ctx, valAddr) require.NoError(t, err) tc.prepareFunc(ctx, validator, delegatorAddr) if tc.blockTime != 0 { ctx = ctx.WithBlockTime(time.Unix(tc.blockTime, 0)) } // We introduce the bug savedAccount := accountKeeper.GetAccount(ctx, delegatorAddr) vestingAccount, ok := savedAccount.(exported.VestingAccount) require.True(t, ok) require.NoError(t, tc.garbageFunc(ctx, vestingAccount, accountKeeper)) m := keeper.NewMigrator(accountKeeper, app.GRPCQueryRouter(), legacySubspace) require.NoError(t, m.Migrate1to2(ctx)) var expVested sdk.Coins var expFree sdk.Coins bondDenom, err := stakingKeeper.BondDenom(ctx) require.NoError(t, err) if tc.expVested != 0 { expVested = sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(tc.expVested))) } if tc.expFree != 0 { expFree = sdk.NewCoins(sdk.NewCoin(bondDenom, sdkmath.NewInt(tc.expFree))) } trackingCorrected( t, ctx, accountKeeper, savedAccount.GetAddress(), expVested, expFree, ) }) } } func trackingCorrected(t *testing.T, ctx sdk.Context, ak keeper.AccountKeeper, addr sdk.AccAddress, expDelVesting, expDelFree sdk.Coins) { t.Helper() baseAccount := ak.GetAccount(ctx, addr) vDA, ok := baseAccount.(exported.VestingAccount) require.True(t, ok) vestedOk := expDelVesting.Equal(vDA.GetDelegatedVesting()) freeOk := expDelFree.Equal(vDA.GetDelegatedFree()) require.True(t, vestedOk, vDA.GetDelegatedVesting().String()) require.True(t, freeOk, vDA.GetDelegatedFree().String()) } func cleartTrackingFields(ctx sdk.Context, vesting exported.VestingAccount, accountKeeper keeper.AccountKeeper) error { switch t := vesting.(type) { case *types.DelayedVestingAccount: t.DelegatedFree = nil t.DelegatedVesting = nil accountKeeper.SetAccount(ctx, t) case *types.ContinuousVestingAccount: t.DelegatedFree = nil t.DelegatedVesting = nil accountKeeper.SetAccount(ctx, t) case *types.PeriodicVestingAccount: t.DelegatedFree = nil t.DelegatedVesting = nil accountKeeper.SetAccount(ctx, t) default: return fmt.Errorf("expected vesting account, found %t", t) } return nil } func dirtyTrackingFields(ctx sdk.Context, vesting exported.VestingAccount, accountKeeper keeper.AccountKeeper) error { dirt := sdk.NewCoins(sdk.NewInt64Coin("stake", 42)) switch t := vesting.(type) { case *types.DelayedVestingAccount: t.DelegatedFree = dirt t.DelegatedVesting = dirt accountKeeper.SetAccount(ctx, t) case *types.ContinuousVestingAccount: t.DelegatedFree = dirt t.DelegatedVesting = dirt accountKeeper.SetAccount(ctx, t) case *types.PeriodicVestingAccount: t.DelegatedFree = dirt t.DelegatedVesting = dirt accountKeeper.SetAccount(ctx, t) default: return fmt.Errorf("expected vesting account, found %t", t) } return nil } func createValidator(t *testing.T, ctx sdk.Context, bankKeeper bankkeeper.Keeper, stakingKeeper *stakingkeeper.Keeper, powers int64) (sdk.AccAddress, sdk.ValAddress) { t.Helper() valTokens := sdk.TokensFromConsensusPower(powers, sdk.DefaultPowerReduction) addrs := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 1, valTokens) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) pks := simtestutil.CreateTestPubKeys(1) val1, err := stakingtypes.NewValidator(valAddrs[0].String(), pks[0], stakingtypes.Description{}) require.NoError(t, err) require.NoError(t, stakingKeeper.SetValidator(ctx, val1)) require.NoError(t, stakingKeeper.SetValidatorByConsAddr(ctx, val1)) require.NoError(t, stakingKeeper.SetNewValidatorByPowerIndex(ctx, val1)) _, err = stakingKeeper.Delegate(ctx, addrs[0], valTokens, stakingtypes.Unbonded, val1, true) require.NoError(t, err) _, err = stakingKeeper.EndBlocker(ctx) require.NoError(t, err) return addrs[0], valAddrs[0] }