diff --git a/CHANGELOG.md b/CHANGELOG.md index deaaa63f5986..955e47976ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#10816](https://github.com/cosmos/cosmos-sdk/pull/10816) Reuse blocked addresses from the bank module. No need to pass them to distribution. * [\#10852](https://github.com/cosmos/cosmos-sdk/pull/10852) Move `x/gov/types` to `x/gov/types/v1beta2`. * [\#10868](https://github.com/cosmos/cosmos-sdk/pull/10868), [\#10989](https://github.com/cosmos/cosmos-sdk/pull/10989), [\#11093](https://github.com/cosmos/cosmos-sdk/pull/11093) The Gov keeper accepts now 2 more mandatory arguments, the ServiceMsgRouter and a gov Config including the max metadata length. +* (x/authz) [\#10447](https://github.com/cosmos/cosmos-sdk/pull/10447) authz `NewGrant` takes a new argument: block time, to correctly validate expire time. ### Client Breaking Changes * [\#11089](https://github.com/cosmos/cosmos-sdk/pull/11089]) interacting with the node through `grpc.Dial` requires clients to pass a codec refer to [doc](docs/run-node/interact-node.md). @@ -184,6 +185,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#9790](https://github.com/cosmos/cosmos-sdk/pull/10687) Fix behavior of `DecCoins.MulDecTruncate`. * [\#10990](https://github.com/cosmos/cosmos-sdk/pull/10990) Fixes missing `iavl-cache-size` config parsing in `GetConfig` method. * (crypto) [#11027] Remove dependency on Tendermint core for xsalsa20symmetric. +* (x/authz) [\#10447](https://github.com/cosmos/cosmos-sdk/pull/10447) Fix authz `NewGrant` expiration check. ### State Machine Breaking diff --git a/x/authz/authorization_grant.go b/x/authz/authorization_grant.go index f5ebf8797be0..30bc1eec467d 100644 --- a/x/authz/authorization_grant.go +++ b/x/authz/authorization_grant.go @@ -9,8 +9,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// NewGrant returns new Grant -func NewGrant(a Authorization, expiration time.Time) (Grant, error) { +// NewGrant returns new Grant. It returns an error if the expiration is before +// the current block time, which is passed into the `blockTime` arg. +func NewGrant(blockTime time.Time, a Authorization, expiration time.Time) (Grant, error) { + if !expiration.After(blockTime) { + return Grant{}, sdkerrors.ErrInvalidRequest.Wrapf("expiration must be after the current block time (%v), got %v", blockTime.Format(time.RFC3339), expiration.Format(time.RFC3339)) + } g := Grant{ Expiration: expiration, } @@ -51,10 +55,6 @@ func (g Grant) GetAuthorization() Authorization { } func (g Grant) ValidateBasic() error { - if g.Expiration.Unix() < time.Now().Unix() { - return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past") - } - av := g.Authorization.GetCachedValue() a, ok := av.(Authorization) if !ok { diff --git a/x/authz/authorization_grant_test.go b/x/authz/authorization_grant_test.go new file mode 100644 index 000000000000..ece0668c2f64 --- /dev/null +++ b/x/authz/authorization_grant_test.go @@ -0,0 +1,43 @@ +package authz + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// TODO: remove and use: robert/expect-error +func expecError(r *require.Assertions, expected string, received error) { + if expected == "" { + r.NoError(received) + } else { + r.Error(received) + r.Contains(received.Error(), expected) + } +} + +func TestNewGrant(t *testing.T) { + a := NewGenericAuthorization("some-type") + var tcs = []struct { + title string + a Authorization + blockTime time.Time + expire time.Time + err string + }{ + {"wrong expire time (1)", a, time.Unix(10, 0), time.Unix(8, 0), "expiration must be after"}, + {"wrong expire time (2)", a, time.Unix(10, 0), time.Unix(10, 0), "expiration must be after"}, + {"good expire time (1)", a, time.Unix(10, 0), time.Unix(10, 1), ""}, + {"good expire time (2)", a, time.Unix(10, 0), time.Unix(11, 0), ""}, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.title, func(t *testing.T) { + _, err := NewGrant(tc.blockTime, tc.a, tc.expire) + expecError(require.New(t), tc.err, err) + }) + } + +} diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index 18531486cc15..fc5b20257ef1 100644 --- a/x/authz/client/cli/tx.go +++ b/x/authz/client/cli/tx.go @@ -56,7 +56,7 @@ func NewCmdGrantAuthorization() *cobra.Command { Use: "grant --from ", Short: "Grant authorization to an address", Long: strings.TrimSpace( - fmt.Sprintf(`grant authorization to an address to execute a transaction on your behalf: + fmt.Sprintf(`create a new grant authorization to an address to execute a transaction on your behalf: Examples: $ %s tx %s grant cosmos1skjw.. send %s --spend-limit=1000stake --from=cosmos1skl.. diff --git a/x/authz/client/testutil/grpc.go b/x/authz/client/testutil/grpc.go index 699dc005d201..577c5350f0c0 100644 --- a/x/authz/client/testutil/grpc.go +++ b/x/authz/client/testutil/grpc.go @@ -107,7 +107,7 @@ func (s *IntegrationTestSuite) TestQueryGrantsGRPC() { false, "", func() { - _, err := ExecGrant(val, []string{ + _, err := CreateGrant(val, []string{ grantee.String(), "generic", fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), diff --git a/x/authz/client/testutil/query.go b/x/authz/client/testutil/query.go index 18355afaf80e..56296576507b 100644 --- a/x/authz/client/testutil/query.go +++ b/x/authz/client/testutil/query.go @@ -20,7 +20,7 @@ func (s *IntegrationTestSuite) TestQueryAuthorizations() { grantee := s.grantee[0] twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -98,7 +98,7 @@ func (s *IntegrationTestSuite) TestQueryAuthorization() { grantee := s.grantee[0] twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), diff --git a/x/authz/client/testutil/test_helpers.go b/x/authz/client/testutil/test_helpers.go index 1a1cd4830fc1..f1a990c54c9a 100644 --- a/x/authz/client/testutil/test_helpers.go +++ b/x/authz/client/testutil/test_helpers.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/authz/client/cli" ) -func ExecGrant(val *network.Validator, args []string) (testutil.BufferWriter, error) { +func CreateGrant(val *network.Validator, args []string) (testutil.BufferWriter, error) { cmd := cli.NewCmdGrantAuthorization() clientCtx := val.ClientCtx return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) diff --git a/x/authz/client/testutil/tx.go b/x/authz/client/testutil/tx.go index e9c0f3881d8f..200cc9f602c3 100644 --- a/x/authz/client/testutil/tx.go +++ b/x/authz/client/testutil/tx.go @@ -65,7 +65,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.msgSendExec(s.grantee[1]) // grant send authorization to grantee2 - out, err := ExecGrant(val, []string{ + out, err := CreateGrant(val, []string{ s.grantee[1].String(), "send", fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), @@ -85,7 +85,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.grantee[2] = s.createAccount("grantee3") // grant send authorization to grantee3 - out, err = ExecGrant(val, []string{ + out, err = CreateGrant(val, []string{ s.grantee[2].String(), "send", fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), @@ -147,8 +147,8 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { val := s.network.Validators[0] grantee := s.grantee[0] - twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - pastHour := time.Now().Add(time.Minute * time.Duration(-60)).Unix() + twoHours := time.Now().Add(time.Minute * 120).Unix() + pastHour := time.Now().Add(-time.Minute * 60).Unix() testCases := []struct { name string @@ -189,7 +189,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { "send", fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=true", flags.FlagBroadcastMode), fmt.Sprintf("--%s=%d", cli.FlagExpiration, pastHour), }, 0, @@ -340,15 +340,14 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { } for _, tc := range testCases { - tc := tc s.Run(tc.name, func() { clientCtx := val.ClientCtx - out, err := ExecGrant( + out, err := CreateGrant( val, tc.args, ) if tc.expectErr { - s.Require().Error(err) + s.Require().Error(err, out) } else { var txResp sdk.TxResponse s.Require().NoError(err) @@ -372,7 +371,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() // send-authorization - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -388,7 +387,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { s.Require().NoError(err) // generic-authorization - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -404,7 +403,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { s.Require().NoError(err) // generic-authorization used for amino testing - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -517,7 +516,7 @@ func (s *IntegrationTestSuite) TestExecAuthorizationWithExpiration() { grantee := s.grantee[0] tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -557,7 +556,7 @@ func (s *IntegrationTestSuite) TestNewExecGenericAuthorized() { grantee := s.grantee[0] twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -660,7 +659,7 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { grantee1 := s.grantee[2] twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -764,7 +763,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { grantee := s.grantee[0] twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -856,7 +855,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { } // test delegate no spend-limit - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -933,7 +932,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { } // test delegating to denied validator - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -968,7 +967,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() // granting undelegate msg authorization - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -1077,7 +1076,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { } // grant undelegate authorization without limit - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go index c3ca01fb633a..933263aebeb2 100644 --- a/x/authz/keeper/keeper.go +++ b/x/authz/keeper/keeper.go @@ -137,7 +137,7 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs [] func (k Keeper) SaveGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization authz.Authorization, expiration time.Time) error { store := ctx.KVStore(k.storeKey) - grant, err := authz.NewGrant(authorization, expiration) + grant, err := authz.NewGrant(ctx.BlockTime(), authorization, expiration) if err != nil { return err } @@ -237,14 +237,20 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *authz.GenesisState { // InitGenesis new authz genesis func (k Keeper) InitGenesis(ctx sdk.Context, data *authz.GenesisState) { for _, entry := range data.Authorization { + if entry.Expiration.Before(ctx.BlockTime()) { + continue + } + grantee, err := sdk.AccAddressFromBech32(entry.Grantee) if err != nil { panic(err) } + granter, err := sdk.AccAddressFromBech32(entry.Granter) if err != nil { panic(err) } + a, ok := entry.Authorization.GetCachedValue().(authz.Authorization) if !ok { panic("expected authorization") diff --git a/x/authz/keeper/keeper_test.go b/x/authz/keeper/keeper_test.go index 539300130bc6..cb9566902cef 100644 --- a/x/authz/keeper/keeper_test.go +++ b/x/authz/keeper/keeper_test.go @@ -55,13 +55,12 @@ func (s *TestSuite) TestKeeper() { s.Require().Nil(authorization) s.Require().Equal(expiration, time.Time{}) now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") x := &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.SaveGrant(ctx, granterAddr, granteeAddr, x, now.Add(-1*time.Hour)) - s.Require().NoError(err) + s.Require().Error(err) authorization, _ = app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, bankSendAuthMsgType) s.Require().Nil(authorization) @@ -105,14 +104,13 @@ func (s *TestSuite) TestKeeperIter() { authorization, expiration := app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, "Abcd") s.Require().Nil(authorization) s.Require().Equal(time.Time{}, expiration) - now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) + now := s.ctx.BlockHeader().Time.Add(time.Second) newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") x := &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, x, now.Add(-1*time.Hour)) - s.Require().NoError(err) + s.Require().Error(err) authorization, _ = app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, "abcd") s.Require().Nil(authorization) @@ -131,8 +129,7 @@ func (s *TestSuite) TestKeeperFees() { granteeAddr := addrs[1] recipientAddr := addrs[2] s.Require().NoError(testutil.FundAccount(app.BankKeeper, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) - now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) + expiration := s.ctx.BlockHeader().Time.Add(1 * time.Second) smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) someCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 123)) @@ -157,7 +154,7 @@ func (s *TestSuite) TestKeeperFees() { s.T().Log("verify dispatch executes with correct information") // grant authorization - err = app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, now) + err = app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, expiration) s.Require().NoError(err) authorization, _ := app.AuthzKeeper.GetCleanAuthorization(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType) s.Require().NotNil(authorization) @@ -206,8 +203,7 @@ func (s *TestSuite) TestDispatchedEvents() { granteeAddr := addrs[1] recipientAddr := addrs[2] require.NoError(testutil.FundAccount(app.BankKeeper, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) - now := s.ctx.BlockHeader().Time - require.NotNil(now) + expiration := s.ctx.BlockHeader().Time.Add(1 * time.Second) // must be in the future smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) msgs := authz.NewMsgExec(granteeAddr, []sdk.Msg{ @@ -219,7 +215,7 @@ func (s *TestSuite) TestDispatchedEvents() { }) // grant authorization - err := app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, now) + err := app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, expiration) require.NoError(err) authorization, _ := app.AuthzKeeper.GetCleanAuthorization(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType) require.NotNil(authorization) diff --git a/x/authz/keeper/msg_server.go b/x/authz/keeper/msg_server.go index 0f8aae3bcaf0..ae0113ac7e6d 100644 --- a/x/authz/keeper/msg_server.go +++ b/x/authz/keeper/msg_server.go @@ -10,7 +10,7 @@ import ( var _ authz.MsgServer = Keeper{} -// GrantAuthorization implements the MsgServer.Grant method. +// GrantAuthorization implements the MsgServer.Grant method to create a new grant. func (k Keeper) Grant(goCtx context.Context, msg *authz.MsgGrant) (*authz.MsgGrantResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) grantee, err := sdk.AccAddressFromBech32(msg.Grantee) diff --git a/x/authz/msgs_test.go b/x/authz/msgs_test.go index 7a41c1befb5d..722ba38f0ca8 100644 --- a/x/authz/msgs_test.go +++ b/x/authz/msgs_test.go @@ -80,7 +80,7 @@ func TestMsgGrantAuthorization(t *testing.T) { {"nil granter and grantee address", nil, nil, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, {"nil authorization", granter, grantee, nil, time.Now(), true, false}, {"valid test case", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 1, 0), false, true}, - {"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, false}, + {"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), true, true}, } for i, tc := range tests { msg, err := authz.NewMsgGrant( diff --git a/x/authz/simulation/decoder_test.go b/x/authz/simulation/decoder_test.go index 4851b6ec2ca2..cb6e1502fb4f 100644 --- a/x/authz/simulation/decoder_test.go +++ b/x/authz/simulation/decoder_test.go @@ -20,7 +20,8 @@ func TestDecodeStore(t *testing.T) { cdc := simapp.MakeTestEncodingConfig().Codec dec := simulation.NewDecodeStore(cdc) - grant, _ := authz.NewGrant(banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), time.Now().UTC()) + now := time.Now().UTC() + grant, _ := authz.NewGrant(now, banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), now.Add(1)) grantBz, err := cdc.Marshal(&grant) require.NoError(t, err) kvPairs := kv.Pairs{ diff --git a/x/authz/simulation/genesis.go b/x/authz/simulation/genesis.go index 2955bf259730..a3a446b376bc 100644 --- a/x/authz/simulation/genesis.go +++ b/x/authz/simulation/genesis.go @@ -2,6 +2,7 @@ package simulation import ( "math/rand" + "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,7 +14,7 @@ import ( ) // genGrant returns a slice of authorization grants. -func genGrant(r *rand.Rand, accounts []simtypes.Account) []authz.GrantAuthorization { +func genGrant(r *rand.Rand, accounts []simtypes.Account, genT time.Time) []authz.GrantAuthorization { authorizations := make([]authz.GrantAuthorization, len(accounts)-1) for i := 0; i < len(accounts)-1; i++ { granter := accounts[i] @@ -22,6 +23,7 @@ func genGrant(r *rand.Rand, accounts []simtypes.Account) []authz.GrantAuthorizat Granter: granter.Address.String(), Grantee: grantee.Address.String(), Authorization: generateRandomGrant(r), + Expiration: genT.AddDate(1, 0, 0), } } @@ -50,7 +52,7 @@ func RandomizedGenState(simState *module.SimulationState) { var grants []authz.GrantAuthorization simState.AppParams.GetOrGenerate( simState.Cdc, "authz", &grants, simState.Rand, - func(r *rand.Rand) { grants = genGrant(r, simState.Accounts) }, + func(r *rand.Rand) { grants = genGrant(r, simState.Accounts, simState.GenTimestamp) }, ) authzGrantsGenesis := authz.NewGenesisState(grants) diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go index 841acb79f66a..b70c3be054a7 100644 --- a/x/authz/simulation/operations.go +++ b/x/authz/simulation/operations.go @@ -1,11 +1,8 @@ package simulation import ( - "fmt" "math/rand" - "github.com/gogo/protobuf/proto" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -49,8 +46,8 @@ func WeightedOperations( var ( weightMsgGrant int - weightRevoke int weightExec int + weightRevoke int ) appParams.GetOrGenerate(cdc, OpWeightMsgGrant, &weightMsgGrant, nil, @@ -59,15 +56,15 @@ func WeightedOperations( }, ) - appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil, + appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil, func(_ *rand.Rand) { - weightRevoke = WeightRevoke + weightExec = WeightExec }, ) - appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil, + appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil, func(_ *rand.Rand) { - weightExec = WeightExec + weightRevoke = WeightRevoke }, ) @@ -76,14 +73,14 @@ func WeightedOperations( weightMsgGrant, SimulateMsgGrant(ak, bk, k), ), - simulation.NewWeightedOperation( - weightRevoke, - SimulateMsgRevoke(ak, bk, k), - ), simulation.NewWeightedOperation( weightExec, SimulateMsgExec(ak, bk, k, appCdc), ), + simulation.NewWeightedOperation( + weightRevoke, + SimulateMsgRevoke(ak, bk, k), + ), } } @@ -236,42 +233,45 @@ func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keepe return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "granter account not found") } - if targetGrant.Expiration.Before(ctx.BlockHeader().Time) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "grant expired"), nil, nil - } - - coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(int64(simtypes.RandIntBetween(r, 100, 1000000))))) + granterspendableCoins := bk.SpendableCoins(ctx, granterAddr) + coins := simtypes.RandSubsetCoins(r, granterspendableCoins) // Check send_enabled status of each sent coin denom if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil } - if targetGrant.Authorization.TypeUrl == fmt.Sprintf("/%s", proto.MessageName(&banktype.SendAuthorization{})) { - sendAuthorization := targetGrant.GetAuthorization().(*banktype.SendAuthorization) - if sendAuthorization.SpendLimit.IsAllLT(coins) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "over spend limit"), nil, nil - } + msg := []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)} + sendAuth, ok := targetGrant.GetAuthorization().(*banktype.SendAuthorization) + if !ok { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "not a send authorization"), nil, nil } - granterspendableCoins := bk.SpendableCoins(ctx, granterAddr) - if granterspendableCoins.IsAllLTE(coins) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "insufficient funds"), nil, nil + if sendAuth.SpendLimit.IsAllLTE(coins) { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "over spend limit"), nil, nil } + res, err := sendAuth.Accept(ctx, msg[0]) + if err != nil { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err + } + + if !res.Accept { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "expired or invalid grant"), nil, nil + } + + msgExec := authz.NewMsgExec(granteeAddr, msg) granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr) fees, err := simtypes.RandomFees(r, ctx, granteeSpendableCoins) if err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err } - - msg := authz.NewMsgExec(granteeAddr, []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)}) txCfg := simappparams.MakeTestEncodingConfig().TxConfig granteeAcc := ak.GetAccount(ctx, granteeAddr) tx, err := helpers.GenTx( txCfg, - []sdk.Msg{&msg}, + []sdk.Msg{&msgExec}, fees, helpers.DefaultGenTxGas, chainID, @@ -288,10 +288,10 @@ func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keepe return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err } - err = msg.UnpackInterfaces(cdc) + err = msgExec.UnpackInterfaces(cdc) if err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err } - return simtypes.NewOperationMsg(&msg, true, "success", nil), nil, nil + return simtypes.NewOperationMsg(&msgExec, true, "success", nil), nil, nil } } diff --git a/x/authz/simulation/operations_test.go b/x/authz/simulation/operations_test.go index 42255c84977e..ac57333bc32c 100644 --- a/x/authz/simulation/operations_test.go +++ b/x/authz/simulation/operations_test.go @@ -43,16 +43,16 @@ func (suite *SimTestSuite) TestWeightedOperations() { // setup 3 accounts s := rand.NewSource(1) r := rand.New(s) - accs := suite.getTestingAccounts(r, 3) + accs := suite.getTestingAccounts(r, 2) expected := []struct { weight int opMsgRoute string opMsgName string }{ - {simulation.WeightGrant, authz.ModuleName, simulation.TypeMsgGrant}, - {simulation.WeightRevoke, authz.ModuleName, simulation.TypeMsgRevoke}, - {simulation.WeightExec, authz.ModuleName, simulation.TypeMsgExec}, + {simulation.WeightGrant, simulation.TypeMsgGrant, simulation.TypeMsgGrant}, + {simulation.WeightExec, simulation.TypeMsgExec, simulation.TypeMsgExec}, + {simulation.WeightRevoke, simulation.TypeMsgRevoke, simulation.TypeMsgRevoke}, } for i, w := range weightedOps {