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, ¬Expired), "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: ¬Expired, 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 }