github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/vesting/msg_server_test.go (about)

     1  package vesting_test
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
     8  	cmttime "github.com/cometbft/cometbft/types/time"
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/stretchr/testify/suite"
    11  
    12  	"cosmossdk.io/math"
    13  	storetypes "cosmossdk.io/store/types"
    14  
    15  	"github.com/cosmos/cosmos-sdk/runtime"
    16  	"github.com/cosmos/cosmos-sdk/testutil"
    17  	sdk "github.com/cosmos/cosmos-sdk/types"
    18  	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
    19  	authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
    20  	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
    21  	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
    22  	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
    23  	vestingtestutil "github.com/cosmos/cosmos-sdk/x/auth/vesting/testutil"
    24  	vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
    25  )
    26  
    27  var (
    28  	fromAddr   = sdk.AccAddress([]byte("from1________________"))
    29  	to1Addr    = sdk.AccAddress([]byte("to1__________________"))
    30  	to2Addr    = sdk.AccAddress([]byte("to2__________________"))
    31  	to3Addr    = sdk.AccAddress([]byte("to3__________________"))
    32  	fooCoin    = sdk.NewInt64Coin("foo", 100)
    33  	periodCoin = sdk.NewInt64Coin("foo", 20)
    34  )
    35  
    36  type VestingTestSuite struct {
    37  	suite.Suite
    38  
    39  	ctx           sdk.Context
    40  	accountKeeper authkeeper.AccountKeeper
    41  	bankKeeper    *vestingtestutil.MockBankKeeper
    42  	msgServer     vestingtypes.MsgServer
    43  }
    44  
    45  func (s *VestingTestSuite) SetupTest() {
    46  	key := storetypes.NewKVStoreKey(authtypes.StoreKey)
    47  	storeService := runtime.NewKVStoreService(key)
    48  	testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
    49  	s.ctx = testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
    50  	encCfg := moduletestutil.MakeTestEncodingConfig()
    51  
    52  	maccPerms := map[string][]string{}
    53  
    54  	ctrl := gomock.NewController(s.T())
    55  	s.bankKeeper = vestingtestutil.NewMockBankKeeper(ctrl)
    56  	s.accountKeeper = authkeeper.NewAccountKeeper(
    57  		encCfg.Codec,
    58  		storeService,
    59  		authtypes.ProtoBaseAccount,
    60  		maccPerms,
    61  		authcodec.NewBech32Codec("cosmos"),
    62  		"cosmos",
    63  		authtypes.NewModuleAddress("gov").String(),
    64  	)
    65  
    66  	vestingtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
    67  	authtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
    68  	s.msgServer = vesting.NewMsgServerImpl(s.accountKeeper, s.bankKeeper)
    69  }
    70  
    71  func (s *VestingTestSuite) TestCreateVestingAccount() {
    72  	testCases := map[string]struct {
    73  		preRun    func()
    74  		input     *vestingtypes.MsgCreateVestingAccount
    75  		expErr    bool
    76  		expErrMsg string
    77  	}{
    78  		"empty from address": {
    79  			input: vestingtypes.NewMsgCreateVestingAccount(
    80  				[]byte{},
    81  				to1Addr,
    82  				sdk.Coins{fooCoin},
    83  				time.Now().Unix(),
    84  				true,
    85  			),
    86  			expErr:    true,
    87  			expErrMsg: "invalid 'from' address",
    88  		},
    89  		"empty to address": {
    90  			input: vestingtypes.NewMsgCreateVestingAccount(
    91  				fromAddr,
    92  				[]byte{},
    93  				sdk.Coins{fooCoin},
    94  				time.Now().Unix(),
    95  				true,
    96  			),
    97  			expErr:    true,
    98  			expErrMsg: "invalid 'to' address",
    99  		},
   100  		"invalid coins": {
   101  			input: vestingtypes.NewMsgCreateVestingAccount(
   102  				fromAddr,
   103  				to1Addr,
   104  				sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
   105  				time.Now().Unix(),
   106  				true,
   107  			),
   108  			expErr:    true,
   109  			expErrMsg: "-1stake: invalid coins",
   110  		},
   111  		"invalid end time": {
   112  			input: vestingtypes.NewMsgCreateVestingAccount(
   113  				fromAddr,
   114  				to1Addr,
   115  				sdk.Coins{fooCoin},
   116  				-10,
   117  				true,
   118  			),
   119  			expErr:    true,
   120  			expErrMsg: "invalid end time",
   121  		},
   122  		"create for existing account": {
   123  			preRun: func() {
   124  				toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
   125  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   126  				s.accountKeeper.SetAccount(s.ctx, toAcc)
   127  				s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
   128  			},
   129  			input: vestingtypes.NewMsgCreateVestingAccount(
   130  				fromAddr,
   131  				to1Addr,
   132  				sdk.Coins{fooCoin},
   133  				time.Now().Unix(),
   134  				true,
   135  			),
   136  			expErr:    true,
   137  			expErrMsg: "already exists",
   138  		},
   139  		"create for blocked account": {
   140  			preRun: func() {
   141  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   142  				s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true)
   143  			},
   144  			input: vestingtypes.NewMsgCreateVestingAccount(
   145  				fromAddr,
   146  				to1Addr,
   147  				sdk.Coins{fooCoin},
   148  				time.Now().Unix(),
   149  				true,
   150  			),
   151  			expErr:    true,
   152  			expErrMsg: "not allowed to receive funds",
   153  		},
   154  		"create a valid delayed vesting account": {
   155  			preRun: func() {
   156  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   157  				s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
   158  				s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil)
   159  			},
   160  			input: vestingtypes.NewMsgCreateVestingAccount(
   161  				fromAddr,
   162  				to2Addr,
   163  				sdk.Coins{fooCoin},
   164  				time.Now().Unix(),
   165  				true,
   166  			),
   167  			expErr:    false,
   168  			expErrMsg: "",
   169  		},
   170  		"create a valid continuous vesting account": {
   171  			preRun: func() {
   172  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   173  				s.bankKeeper.EXPECT().BlockedAddr(to3Addr).Return(false)
   174  				s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to3Addr, sdk.Coins{fooCoin}).Return(nil)
   175  			},
   176  			input: vestingtypes.NewMsgCreateVestingAccount(
   177  				fromAddr,
   178  				to3Addr,
   179  				sdk.Coins{fooCoin},
   180  				time.Now().Unix(),
   181  				false,
   182  			),
   183  			expErr:    false,
   184  			expErrMsg: "",
   185  		},
   186  	}
   187  
   188  	for name, tc := range testCases {
   189  		s.Run(name, func() {
   190  			if tc.preRun != nil {
   191  				tc.preRun()
   192  			}
   193  			_, err := s.msgServer.CreateVestingAccount(s.ctx, tc.input)
   194  			if tc.expErr {
   195  				s.Require().Error(err)
   196  				s.Require().Contains(err.Error(), tc.expErrMsg)
   197  			} else {
   198  				s.Require().NoError(err)
   199  			}
   200  		})
   201  	}
   202  }
   203  
   204  func (s *VestingTestSuite) TestCreatePermanentLockedAccount() {
   205  	testCases := map[string]struct {
   206  		preRun    func()
   207  		input     *vestingtypes.MsgCreatePermanentLockedAccount
   208  		expErr    bool
   209  		expErrMsg string
   210  	}{
   211  		"empty from address": {
   212  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   213  				[]byte{},
   214  				to1Addr,
   215  				sdk.Coins{fooCoin},
   216  			),
   217  			expErr:    true,
   218  			expErrMsg: "invalid 'from' address",
   219  		},
   220  		"empty to address": {
   221  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   222  				fromAddr,
   223  				[]byte{},
   224  				sdk.Coins{fooCoin},
   225  			),
   226  			expErr:    true,
   227  			expErrMsg: "invalid 'to' address",
   228  		},
   229  		"invalid coins": {
   230  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   231  				fromAddr,
   232  				to1Addr,
   233  				sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
   234  			),
   235  			expErr:    true,
   236  			expErrMsg: "-1stake: invalid coins",
   237  		},
   238  		"create for existing account": {
   239  			preRun: func() {
   240  				toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
   241  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   242  				s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
   243  				s.accountKeeper.SetAccount(s.ctx, toAcc)
   244  			},
   245  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   246  				fromAddr,
   247  				to1Addr,
   248  				sdk.Coins{fooCoin},
   249  			),
   250  			expErr:    true,
   251  			expErrMsg: "already exists",
   252  		},
   253  		"create for blocked account": {
   254  			preRun: func() {
   255  				toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
   256  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   257  				s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true)
   258  				s.accountKeeper.SetAccount(s.ctx, toAcc)
   259  			},
   260  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   261  				fromAddr,
   262  				to1Addr,
   263  				sdk.Coins{fooCoin},
   264  			),
   265  			expErr:    true,
   266  			expErrMsg: "not allowed to receive funds",
   267  		},
   268  
   269  		"create a valid permanent locked account": {
   270  			preRun: func() {
   271  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
   272  				s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
   273  				s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil)
   274  			},
   275  			input: vestingtypes.NewMsgCreatePermanentLockedAccount(
   276  				fromAddr,
   277  				to2Addr,
   278  				sdk.Coins{fooCoin},
   279  			),
   280  			expErr:    false,
   281  			expErrMsg: "",
   282  		},
   283  	}
   284  
   285  	for name, tc := range testCases {
   286  		s.Run(name, func() {
   287  			if tc.preRun != nil {
   288  				tc.preRun()
   289  			}
   290  
   291  			_, err := s.msgServer.CreatePermanentLockedAccount(s.ctx, tc.input)
   292  			if tc.expErr {
   293  				s.Require().Error(err)
   294  				s.Require().Contains(err.Error(), tc.expErrMsg)
   295  			} else {
   296  				s.Require().NoError(err)
   297  			}
   298  		})
   299  	}
   300  }
   301  
   302  func (s *VestingTestSuite) TestCreatePeriodicVestingAccount() {
   303  	testCases := []struct {
   304  		name      string
   305  		preRun    func()
   306  		input     *vestingtypes.MsgCreatePeriodicVestingAccount
   307  		expErr    bool
   308  		expErrMsg string
   309  	}{
   310  		{
   311  			name: "empty from address",
   312  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   313  				[]byte{},
   314  				to1Addr,
   315  				time.Now().Unix(),
   316  				[]vestingtypes.Period{
   317  					{
   318  						Length: 10,
   319  						Amount: sdk.NewCoins(periodCoin),
   320  					},
   321  				},
   322  			),
   323  			expErr:    true,
   324  			expErrMsg: "invalid 'from' address",
   325  		},
   326  		{
   327  			name: "empty to address",
   328  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   329  				fromAddr,
   330  				[]byte{},
   331  				time.Now().Unix(),
   332  				[]vestingtypes.Period{
   333  					{
   334  						Length: 10,
   335  						Amount: sdk.NewCoins(periodCoin),
   336  					},
   337  				},
   338  			),
   339  			expErr:    true,
   340  			expErrMsg: "invalid 'to' address",
   341  		},
   342  		{
   343  			name: "invalid start time",
   344  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   345  				fromAddr,
   346  				to1Addr,
   347  				0,
   348  				[]vestingtypes.Period{
   349  					{
   350  						Length: 10,
   351  						Amount: sdk.NewCoins(periodCoin),
   352  					},
   353  				},
   354  			),
   355  			expErr:    true,
   356  			expErrMsg: "invalid start time",
   357  		},
   358  		{
   359  			name: "invalid period",
   360  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   361  				fromAddr,
   362  				to1Addr,
   363  				time.Now().Unix(),
   364  				[]vestingtypes.Period{
   365  					{
   366  						Length: 0,
   367  						Amount: sdk.NewCoins(periodCoin),
   368  					},
   369  				},
   370  			),
   371  			expErr:    true,
   372  			expErrMsg: "invalid period",
   373  		},
   374  		{
   375  			name: "invalid coins",
   376  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   377  				fromAddr,
   378  				to1Addr,
   379  				time.Now().Unix(),
   380  				[]vestingtypes.Period{
   381  					{
   382  						Length: 1,
   383  						Amount: sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
   384  					},
   385  				},
   386  			),
   387  			expErr:    true,
   388  			expErrMsg: "-1stake: invalid coins",
   389  		},
   390  		{
   391  			name: "create for existing account",
   392  			preRun: func() {
   393  				s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
   394  				toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
   395  				s.accountKeeper.SetAccount(s.ctx, toAcc)
   396  			},
   397  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   398  				fromAddr,
   399  				to1Addr,
   400  				time.Now().Unix(),
   401  				[]vestingtypes.Period{
   402  					{
   403  						Length: 10,
   404  						Amount: sdk.NewCoins(periodCoin),
   405  					},
   406  				},
   407  			),
   408  			expErr:    true,
   409  			expErrMsg: "already exists",
   410  		},
   411  		{
   412  			name: "create for blocked address",
   413  			preRun: func() {
   414  				s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(true)
   415  			},
   416  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   417  				fromAddr,
   418  				to2Addr,
   419  				time.Now().Unix(),
   420  				[]vestingtypes.Period{
   421  					{
   422  						Length: 10,
   423  						Amount: sdk.NewCoins(periodCoin),
   424  					},
   425  					{
   426  						Length: 20,
   427  						Amount: sdk.NewCoins(fooCoin),
   428  					},
   429  				},
   430  			),
   431  			expErr:    true,
   432  			expErrMsg: "not allowed to receive funds",
   433  		},
   434  		{
   435  			name: "create a valid periodic vesting account",
   436  			preRun: func() {
   437  				s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), periodCoin.Add(fooCoin)).Return(nil)
   438  				s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
   439  				s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, gomock.Any()).Return(nil)
   440  			},
   441  			input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
   442  				fromAddr,
   443  				to2Addr,
   444  				time.Now().Unix(),
   445  				[]vestingtypes.Period{
   446  					{
   447  						Length: 10,
   448  						Amount: sdk.NewCoins(periodCoin),
   449  					},
   450  					{
   451  						Length: 20,
   452  						Amount: sdk.NewCoins(fooCoin),
   453  					},
   454  				},
   455  			),
   456  			expErr:    false,
   457  			expErrMsg: "",
   458  		},
   459  	}
   460  
   461  	for _, tc := range testCases {
   462  		s.Run(tc.name, func() {
   463  			if tc.preRun != nil {
   464  				tc.preRun()
   465  			}
   466  			_, err := s.msgServer.CreatePeriodicVestingAccount(s.ctx, tc.input)
   467  			if tc.expErr {
   468  				s.Require().Error(err)
   469  				s.Require().Contains(err.Error(), tc.expErrMsg)
   470  			} else {
   471  				s.Require().NoError(err)
   472  			}
   473  		})
   474  	}
   475  }
   476  
   477  func TestVestingTestSuite(t *testing.T) {
   478  	suite.Run(t, new(VestingTestSuite))
   479  }