github.com/cosmos/cosmos-sdk@v0.50.10/x/authz/keeper/keeper_test.go (about)

     1  package keeper_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/log"
    13  	storetypes "cosmossdk.io/store/types"
    14  
    15  	"github.com/cosmos/cosmos-sdk/baseapp"
    16  	"github.com/cosmos/cosmos-sdk/codec/address"
    17  	"github.com/cosmos/cosmos-sdk/runtime"
    18  	"github.com/cosmos/cosmos-sdk/testutil"
    19  	simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
    20  	sdk "github.com/cosmos/cosmos-sdk/types"
    21  	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
    22  	"github.com/cosmos/cosmos-sdk/x/authz"
    23  	authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
    24  	authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module"
    25  	authztestutil "github.com/cosmos/cosmos-sdk/x/authz/testutil"
    26  	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    27  )
    28  
    29  var (
    30  	bankSendAuthMsgType = banktypes.SendAuthorization{}.MsgTypeURL()
    31  	coins10             = sdk.NewCoins(sdk.NewInt64Coin("stake", 10))
    32  	coins100            = sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
    33  	coins1000           = sdk.NewCoins(sdk.NewInt64Coin("stake", 1000))
    34  )
    35  
    36  type TestSuite struct {
    37  	suite.Suite
    38  
    39  	ctx           sdk.Context
    40  	addrs         []sdk.AccAddress
    41  	authzKeeper   authzkeeper.Keeper
    42  	accountKeeper *authztestutil.MockAccountKeeper
    43  	bankKeeper    *authztestutil.MockBankKeeper
    44  	baseApp       *baseapp.BaseApp
    45  	encCfg        moduletestutil.TestEncodingConfig
    46  	queryClient   authz.QueryClient
    47  	msgSrvr       authz.MsgServer
    48  }
    49  
    50  func (s *TestSuite) SetupTest() {
    51  	key := storetypes.NewKVStoreKey(authzkeeper.StoreKey)
    52  	storeService := runtime.NewKVStoreService(key)
    53  	testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
    54  	s.ctx = testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()})
    55  	s.encCfg = moduletestutil.MakeTestEncodingConfig(authzmodule.AppModuleBasic{})
    56  
    57  	s.baseApp = baseapp.NewBaseApp(
    58  		"authz",
    59  		log.NewNopLogger(),
    60  		testCtx.DB,
    61  		s.encCfg.TxConfig.TxDecoder(),
    62  	)
    63  	s.baseApp.SetCMS(testCtx.CMS)
    64  	s.baseApp.SetInterfaceRegistry(s.encCfg.InterfaceRegistry)
    65  
    66  	s.addrs = simtestutil.CreateIncrementalAccounts(7)
    67  
    68  	// gomock initializations
    69  	ctrl := gomock.NewController(s.T())
    70  	s.accountKeeper = authztestutil.NewMockAccountKeeper(ctrl)
    71  	s.accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
    72  
    73  	s.bankKeeper = authztestutil.NewMockBankKeeper(ctrl)
    74  	banktypes.RegisterInterfaces(s.encCfg.InterfaceRegistry)
    75  	banktypes.RegisterMsgServer(s.baseApp.MsgServiceRouter(), s.bankKeeper)
    76  	s.bankKeeper.EXPECT().BlockedAddr(gomock.Any()).Return(false).AnyTimes()
    77  
    78  	s.authzKeeper = authzkeeper.NewKeeper(storeService, s.encCfg.Codec, s.baseApp.MsgServiceRouter(), s.accountKeeper).SetBankKeeper(s.bankKeeper)
    79  
    80  	queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.encCfg.InterfaceRegistry)
    81  	authz.RegisterQueryServer(queryHelper, s.authzKeeper)
    82  	queryClient := authz.NewQueryClient(queryHelper)
    83  	s.queryClient = queryClient
    84  
    85  	s.msgSrvr = s.authzKeeper
    86  }
    87  
    88  func (s *TestSuite) TestKeeper() {
    89  	ctx, addrs := s.ctx, s.addrs
    90  	now := ctx.BlockTime()
    91  	require := s.Require()
    92  
    93  	granterAddr := addrs[0]
    94  	granteeAddr := addrs[1]
    95  
    96  	s.T().Log("verify that no authorization returns nil")
    97  	authorizations, err := s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
    98  	require.NoError(err)
    99  	require.Len(authorizations, 0)
   100  
   101  	s.T().Log("verify save, get and delete")
   102  	sendAutz := &banktypes.SendAuthorization{SpendLimit: coins100}
   103  	expire := now.AddDate(1, 0, 0)
   104  	err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
   105  	require.NoError(err)
   106  
   107  	authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
   108  	require.NoError(err)
   109  	require.Len(authorizations, 1)
   110  
   111  	err = s.authzKeeper.DeleteGrant(ctx, granteeAddr, granterAddr, sendAutz.MsgTypeURL())
   112  	require.NoError(err)
   113  
   114  	authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
   115  	require.NoError(err)
   116  	require.Len(authorizations, 0)
   117  
   118  	s.T().Log("verify granting same authorization overwrite existing authorization")
   119  	err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
   120  	require.NoError(err)
   121  
   122  	authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
   123  	require.NoError(err)
   124  	require.Len(authorizations, 1)
   125  
   126  	sendAutz = &banktypes.SendAuthorization{SpendLimit: coins1000}
   127  	err = s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAutz, &expire)
   128  	require.NoError(err)
   129  	authorizations, err = s.authzKeeper.GetAuthorizations(ctx, granteeAddr, granterAddr)
   130  	require.NoError(err)
   131  	require.Len(authorizations, 1)
   132  	authorization := authorizations[0]
   133  	sendAuth := authorization.(*banktypes.SendAuthorization)
   134  	require.Equal(sendAuth.SpendLimit, sendAutz.SpendLimit)
   135  	require.Equal(sendAuth.MsgTypeURL(), sendAutz.MsgTypeURL())
   136  
   137  	s.T().Log("verify removing non existing authorization returns error")
   138  	err = s.authzKeeper.DeleteGrant(ctx, granterAddr, granteeAddr, "abcd")
   139  	s.Require().Error(err)
   140  }
   141  
   142  func (s *TestSuite) TestKeeperIter() {
   143  	ctx, addrs := s.ctx, s.addrs
   144  
   145  	granterAddr := addrs[0]
   146  	granteeAddr := addrs[1]
   147  	granter2Addr := addrs[2]
   148  	e := ctx.BlockTime().AddDate(1, 0, 0)
   149  	sendAuthz := banktypes.NewSendAuthorization(coins100, nil)
   150  
   151  	s.authzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, sendAuthz, &e)
   152  	s.authzKeeper.SaveGrant(ctx, granteeAddr, granter2Addr, sendAuthz, &e)
   153  
   154  	s.authzKeeper.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
   155  		s.Require().Equal(granteeAddr, grantee)
   156  		s.Require().Contains([]sdk.AccAddress{granterAddr, granter2Addr}, granter)
   157  		return true
   158  	})
   159  }
   160  
   161  func (s *TestSuite) TestDispatchAction() {
   162  	addrs := s.addrs
   163  	require := s.Require()
   164  	now := s.ctx.BlockTime()
   165  
   166  	granterAddr := addrs[0]
   167  	granteeAddr := addrs[1]
   168  	recipientAddr := addrs[2]
   169  	a := banktypes.NewSendAuthorization(coins100, nil)
   170  
   171  	testCases := []struct {
   172  		name      string
   173  		req       authz.MsgExec
   174  		expectErr bool
   175  		errMsg    string
   176  		preRun    func() sdk.Context
   177  		postRun   func()
   178  	}{
   179  		{
   180  			"expect error authorization not found",
   181  			authz.NewMsgExec(granteeAddr, []sdk.Msg{
   182  				&banktypes.MsgSend{
   183  					Amount:      coins10,
   184  					FromAddress: granterAddr.String(),
   185  					ToAddress:   recipientAddr.String(),
   186  				},
   187  			}),
   188  			true,
   189  			"authorization not found",
   190  			func() sdk.Context {
   191  				// remove any existing authorizations
   192  				s.authzKeeper.DeleteGrant(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType)
   193  				return s.ctx
   194  			},
   195  			func() {},
   196  		},
   197  		{
   198  			"expect error expired authorization",
   199  			authz.NewMsgExec(granteeAddr, []sdk.Msg{
   200  				&banktypes.MsgSend{
   201  					Amount:      coins10,
   202  					FromAddress: granterAddr.String(),
   203  					ToAddress:   recipientAddr.String(),
   204  				},
   205  			}),
   206  			true,
   207  			"authorization expired",
   208  			func() sdk.Context {
   209  				e := now.AddDate(0, 0, 1)
   210  				err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
   211  				require.NoError(err)
   212  				return s.ctx.WithBlockTime(s.ctx.BlockTime().AddDate(0, 0, 2))
   213  			},
   214  			func() {},
   215  		},
   216  		{
   217  			"expect error over spent limit",
   218  			authz.NewMsgExec(granteeAddr, []sdk.Msg{
   219  				&banktypes.MsgSend{
   220  					Amount:      coins1000,
   221  					FromAddress: granterAddr.String(),
   222  					ToAddress:   recipientAddr.String(),
   223  				},
   224  			}),
   225  			true,
   226  			"requested amount is more than spend limit",
   227  			func() sdk.Context {
   228  				e := now.AddDate(0, 1, 0)
   229  				err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
   230  				require.NoError(err)
   231  				return s.ctx
   232  			},
   233  			func() {},
   234  		},
   235  		{
   236  			"valid test verify amount left",
   237  			authz.NewMsgExec(granteeAddr, []sdk.Msg{
   238  				&banktypes.MsgSend{
   239  					Amount:      coins10,
   240  					FromAddress: granterAddr.String(),
   241  					ToAddress:   recipientAddr.String(),
   242  				},
   243  			}),
   244  			false,
   245  			"",
   246  			func() sdk.Context {
   247  				e := now.AddDate(0, 1, 0)
   248  				err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
   249  				require.NoError(err)
   250  				return s.ctx
   251  			},
   252  			func() {
   253  				authzs, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
   254  				require.NoError(err)
   255  				require.Len(authzs, 1)
   256  				authorization := authzs[0].(*banktypes.SendAuthorization)
   257  				require.NotNil(authorization)
   258  				require.Equal(authorization.SpendLimit, coins100.Sub(coins10...))
   259  			},
   260  		},
   261  		{
   262  			"valid test verify authorization is removed when it is used up",
   263  			authz.NewMsgExec(granteeAddr, []sdk.Msg{
   264  				&banktypes.MsgSend{
   265  					Amount:      coins100,
   266  					FromAddress: granterAddr.String(),
   267  					ToAddress:   recipientAddr.String(),
   268  				},
   269  			}),
   270  			false,
   271  			"",
   272  			func() sdk.Context {
   273  				e := now.AddDate(0, 1, 0)
   274  				err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, a, &e)
   275  				require.NoError(err)
   276  				return s.ctx
   277  			},
   278  			func() {
   279  				authzs, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
   280  				require.NoError(err)
   281  				require.Len(authzs, 0)
   282  			},
   283  		},
   284  	}
   285  
   286  	for _, tc := range testCases {
   287  		s.Run(tc.name, func() {
   288  			ctx := tc.preRun()
   289  			executeMsgs, err := tc.req.GetMessages()
   290  			require.NoError(err)
   291  			result, err := s.authzKeeper.DispatchActions(ctx, granteeAddr, executeMsgs)
   292  			if tc.expectErr {
   293  				require.Error(err)
   294  				require.Nil(result)
   295  				require.Contains(err.Error(), tc.errMsg)
   296  			} else {
   297  				require.NoError(err)
   298  				require.NotNil(result)
   299  			}
   300  			tc.postRun()
   301  		})
   302  	}
   303  }
   304  
   305  // Tests that all msg events included in an authz MsgExec tx
   306  // Ref: https://github.com/cosmos/cosmos-sdk/issues/9501
   307  func (s *TestSuite) TestDispatchedEvents() {
   308  	require := s.Require()
   309  	addrs := s.addrs
   310  	granterAddr := addrs[0]
   311  	granteeAddr := addrs[1]
   312  	recipientAddr := addrs[2]
   313  	expiration := s.ctx.BlockTime().Add(1 * time.Second) // must be in the future
   314  
   315  	msgs := authz.NewMsgExec(granteeAddr, []sdk.Msg{
   316  		&banktypes.MsgSend{
   317  			Amount:      coins10,
   318  			FromAddress: granterAddr.String(),
   319  			ToAddress:   recipientAddr.String(),
   320  		},
   321  	})
   322  
   323  	// grant authorization
   324  	err := s.authzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: coins10}, &expiration)
   325  	require.NoError(err)
   326  	authorizations, err := s.authzKeeper.GetAuthorizations(s.ctx, granteeAddr, granterAddr)
   327  	require.NoError(err)
   328  	require.Len(authorizations, 1)
   329  	authorization := authorizations[0].(*banktypes.SendAuthorization)
   330  	require.Equal(authorization.MsgTypeURL(), bankSendAuthMsgType)
   331  
   332  	executeMsgs, err := msgs.GetMessages()
   333  	require.NoError(err)
   334  
   335  	result, err := s.authzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs)
   336  	require.NoError(err)
   337  	require.NotNil(result)
   338  
   339  	events := s.ctx.EventManager().Events()
   340  
   341  	// get last 5 events (events that occur *after* the grant)
   342  	events = events[len(events)-2:]
   343  
   344  	requiredEvents := map[string]bool{
   345  		"cosmos.authz.v1beta1.EventGrant":  true,
   346  		"cosmos.authz.v1beta1.EventRevoke": true,
   347  	}
   348  	for _, e := range events {
   349  		requiredEvents[e.Type] = true
   350  	}
   351  	for _, v := range requiredEvents {
   352  		require.True(v)
   353  	}
   354  }
   355  
   356  func (s *TestSuite) TestDequeueAllGrantsQueue() {
   357  	require := s.Require()
   358  	addrs := s.addrs
   359  	granter := addrs[0]
   360  	grantee := addrs[1]
   361  	grantee1 := addrs[2]
   362  	exp := s.ctx.BlockTime().AddDate(0, 0, 1)
   363  	a := banktypes.SendAuthorization{SpendLimit: coins100}
   364  
   365  	// create few authorizations
   366  	err := s.authzKeeper.SaveGrant(s.ctx, grantee, granter, &a, &exp)
   367  	require.NoError(err)
   368  
   369  	err = s.authzKeeper.SaveGrant(s.ctx, grantee1, granter, &a, &exp)
   370  	require.NoError(err)
   371  
   372  	exp2 := exp.AddDate(0, 1, 0)
   373  	err = s.authzKeeper.SaveGrant(s.ctx, granter, grantee1, &a, &exp2)
   374  	require.NoError(err)
   375  
   376  	exp2 = exp.AddDate(2, 0, 0)
   377  	err = s.authzKeeper.SaveGrant(s.ctx, granter, grantee, &a, &exp2)
   378  	require.NoError(err)
   379  
   380  	newCtx := s.ctx.WithBlockTime(exp.AddDate(1, 0, 0))
   381  	err = s.authzKeeper.DequeueAndDeleteExpiredGrants(newCtx)
   382  	require.NoError(err)
   383  
   384  	s.T().Log("verify expired grants are pruned from the state")
   385  	authzs, err := s.authzKeeper.GetAuthorizations(newCtx, grantee, granter)
   386  	require.NoError(err)
   387  	require.Len(authzs, 0)
   388  
   389  	authzs, err = s.authzKeeper.GetAuthorizations(newCtx, granter, grantee1)
   390  	require.NoError(err)
   391  	require.Len(authzs, 0)
   392  
   393  	authzs, err = s.authzKeeper.GetAuthorizations(newCtx, grantee1, granter)
   394  	require.NoError(err)
   395  	require.Len(authzs, 0)
   396  
   397  	authzs, err = s.authzKeeper.GetAuthorizations(newCtx, granter, grantee)
   398  	require.NoError(err)
   399  	require.Len(authzs, 1)
   400  }
   401  
   402  func (s *TestSuite) TestGetAuthorization() {
   403  	addr1 := s.addrs[3]
   404  	addr2 := s.addrs[4]
   405  	addr3 := s.addrs[5]
   406  	addr4 := s.addrs[6]
   407  
   408  	genAuthMulti := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgMultiSend{}))
   409  	genAuthSend := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgSend{}))
   410  	sendAuth := banktypes.NewSendAuthorization(coins10, nil)
   411  
   412  	start := s.ctx.BlockHeader().Time
   413  	expired := start.Add(time.Duration(1) * time.Second)
   414  	notExpired := start.Add(time.Duration(5) * time.Hour)
   415  
   416  	s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr2, genAuthMulti, nil), "creating grant 1->2")
   417  	s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr3, genAuthSend, &expired), "creating grant 1->3")
   418  	s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr4, sendAuth, &notExpired), "creating grant 1->4")
   419  	// Without access to private keeper methods, I don't know how to save a grant with an invalid authorization.
   420  	newCtx := s.ctx.WithBlockTime(start.Add(time.Duration(1) * time.Minute))
   421  
   422  	tests := []struct {
   423  		name    string
   424  		grantee sdk.AccAddress
   425  		granter sdk.AccAddress
   426  		msgType string
   427  		expAuth authz.Authorization
   428  		expExp  *time.Time
   429  	}{
   430  		{
   431  			name:    "grant has nil exp and is returned",
   432  			grantee: addr1,
   433  			granter: addr2,
   434  			msgType: genAuthMulti.MsgTypeURL(),
   435  			expAuth: genAuthMulti,
   436  			expExp:  nil,
   437  		},
   438  		{
   439  			name:    "grant is expired not returned",
   440  			grantee: addr1,
   441  			granter: addr3,
   442  			msgType: genAuthSend.MsgTypeURL(),
   443  			expAuth: nil,
   444  			expExp:  nil,
   445  		},
   446  		{
   447  			name:    "grant is not expired and is returned",
   448  			grantee: addr1,
   449  			granter: addr4,
   450  			msgType: sendAuth.MsgTypeURL(),
   451  			expAuth: sendAuth,
   452  			expExp:  &notExpired,
   453  		},
   454  		{
   455  			name:    "grant is not expired but wrong msg type returns nil",
   456  			grantee: addr1,
   457  			granter: addr4,
   458  			msgType: genAuthMulti.MsgTypeURL(),
   459  			expAuth: nil,
   460  			expExp:  nil,
   461  		},
   462  		{
   463  			name:    "no grant exists between the two",
   464  			grantee: addr2,
   465  			granter: addr3,
   466  			msgType: genAuthSend.MsgTypeURL(),
   467  			expAuth: nil,
   468  			expExp:  nil,
   469  		},
   470  	}
   471  
   472  	for _, tc := range tests {
   473  		s.Run(tc.name, func() {
   474  			actAuth, actExp := s.authzKeeper.GetAuthorization(newCtx, tc.grantee, tc.granter, tc.msgType)
   475  			s.Assert().Equal(tc.expAuth, actAuth, "authorization")
   476  			s.Assert().Equal(tc.expExp, actExp, "expiration")
   477  		})
   478  	}
   479  }
   480  
   481  func (s *TestSuite) TestGetAuthorizations() {
   482  	require := s.Require()
   483  	addr1 := s.addrs[1]
   484  	addr2 := s.addrs[2]
   485  
   486  	genAuthMulti := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgMultiSend{}))
   487  	genAuthSend := authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgSend{}))
   488  
   489  	start := s.ctx.BlockHeader().Time
   490  	expired := start.Add(time.Duration(1) * time.Second)
   491  
   492  	s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr2, genAuthMulti, &expired), "creating multi send grant 1->2")
   493  	s.Require().NoError(s.authzKeeper.SaveGrant(s.ctx, addr1, addr2, genAuthSend, &expired), "creating send grant 1->2")
   494  
   495  	authzs, err := s.authzKeeper.GetAuthorizations(s.ctx, addr1, addr2)
   496  	require.NoError(err)
   497  	require.Len(authzs, 2)
   498  	require.Equal(sdk.MsgTypeURL(&banktypes.MsgMultiSend{}), authzs[0].MsgTypeURL())
   499  	require.Equal(sdk.MsgTypeURL(&banktypes.MsgSend{}), authzs[1].MsgTypeURL())
   500  }
   501  
   502  func TestTestSuite(t *testing.T) {
   503  	suite.Run(t, new(TestSuite))
   504  }