From 3ab1bc234642978eb17b36edaca62840bc430773 Mon Sep 17 00:00:00 2001 From: Marie Gauthier Date: Tue, 27 Apr 2021 15:37:26 +0200 Subject: [PATCH] x/feegrant completeness audit updates (#9177) * Updates to Genesis * Move grpc_query tests to keeper pkg * Unify CLI command usage message * Use timestamp for time based expiration in cli * Update FlagExpiration * Update wording * Add test case for invalid expiration * Update cli Long description * Update exp date examples * Use constants --- x/authz/client/cli/tx.go | 2 +- x/feegrant/client/cli/tx.go | 29 +-- x/feegrant/client/rest/grpc_query_test.go | 210 ---------------------- x/feegrant/client/testutil/suite.go | 54 ++++-- x/feegrant/genesis.go | 29 +-- x/feegrant/genesis_test.go | 52 +++++- x/feegrant/keeper/grpc_query_test.go | 162 +++++++++++++++++ x/feegrant/module.go | 5 +- 8 files changed, 280 insertions(+), 263 deletions(-) delete mode 100644 x/feegrant/client/rest/grpc_query_test.go create mode 100644 x/feegrant/keeper/grpc_query_test.go diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index 44df6f5e9aa4..1b74e120f043 100644 --- a/x/authz/client/cli/tx.go +++ b/x/authz/client/cli/tx.go @@ -186,7 +186,7 @@ Examples: func NewCmdRevokeAuthorization() *cobra.Command { cmd := &cobra.Command{ - Use: "revoke [grantee_address] [msg_type] --from=[granter_address]", + Use: "revoke [grantee] [msg_type] --from=[granter]", Short: "revoke authorization", Long: strings.TrimSpace( fmt.Sprintf(`revoke authorization from a granter to a grantee: diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go index c355ca4b5acd..2d38cd053082 100644 --- a/x/feegrant/client/cli/tx.go +++ b/x/feegrant/client/cli/tx.go @@ -55,9 +55,9 @@ func NewCmdFeeGrant() *cobra.Command { ignored as it is implied from [granter]. Examples: -%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000 or +%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 2022-01-30T15:04:05Z or %s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000 or -%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000 +%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 2022-01-30T15:04:05Z --allowed-messages "/cosmos.gov.v1beta1.Msg/SubmitProposal,/cosmos.gov.v1beta1.Msg/Vote" `, version.AppName, types.ModuleName, version.AppName, types.ModuleName, version.AppName, types.ModuleName, ), @@ -92,7 +92,7 @@ Examples: return err } - exp, err := cmd.Flags().GetInt64(FlagExpiration) + exp, err := cmd.Flags().GetString(FlagExpiration) if err != nil { return err } @@ -101,9 +101,13 @@ Examples: SpendLimit: limit, } - if exp != 0 { - expDuration := time.Duration(exp) * time.Second - basic.Expiration = types.ExpiresAtTime(time.Now().Add(expDuration)) + var expiresAtTime time.Time + if exp != "" { + expiresAtTime, err = time.Parse(time.RFC3339, exp) + if err != nil { + return err + } + basic.Expiration = types.ExpiresAtTime(expiresAtTime) } var grant types.FeeAllowanceI @@ -127,14 +131,15 @@ Examples: } if periodClock > 0 && periodLimit != nil { - if exp > 0 && periodClock > exp { - return fmt.Errorf("period(%d) cannot be greater than the expiration(%d)", periodClock, exp) + periodReset := time.Now().Add(time.Duration(periodClock) * time.Second) + if exp != "" && periodReset.Sub(expiresAtTime) > 0 { + return fmt.Errorf("period(%d) cannot reset after expiration(%v)", periodClock, exp) } periodic := types.PeriodicFeeAllowance{ Basic: basic, Period: types.ClockDuration(time.Duration(periodClock) * time.Second), - PeriodReset: types.ExpiresAtTime(time.Now().Add(time.Duration(periodClock) * time.Second)), + PeriodReset: types.ExpiresAtTime(periodReset), PeriodSpendLimit: periodLimit, PeriodCanSpend: periodLimit, } @@ -176,7 +181,7 @@ Examples: flags.AddTxFlagsToCmd(cmd) cmd.Flags().StringSlice(FlagAllowedMsgs, []string{}, "Set of allowed messages for fee allowance") - cmd.Flags().Int64(FlagExpiration, 0, "The second unit of time duration which the grant is active for the user") + cmd.Flags().String(FlagExpiration, "", "The RFC 3339 timestamp after which the grant expires for the user") cmd.Flags().String(FlagSpendLimit, "", "Spend limit specifies the max limit can be used, if not mentioned there is no limit") cmd.Flags().Int64(FlagPeriod, 0, "period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset") cmd.Flags().String(FlagPeriodLimit, "", "period limit specifies the maximum number of coins that can be spent in the period") @@ -187,11 +192,11 @@ Examples: // NewCmdRevokeFeegrant returns a CLI command handler for creating a MsgRevokeFeeAllowance transaction. func NewCmdRevokeFeegrant() *cobra.Command { cmd := &cobra.Command{ - Use: "revoke [granter_address] [grantee_address]", + Use: "revoke [granter] [grantee]", Short: "revoke fee-grant", Long: strings.TrimSpace( fmt.Sprintf(`revoke fee grant from a granter to a grantee. Note, the'--from' flag is - ignored as it is implied from [granter_address]. + ignored as it is implied from [granter]. Example: $ %s tx %s revoke cosmos1skj.. cosmos1skj.. diff --git a/x/feegrant/client/rest/grpc_query_test.go b/x/feegrant/client/rest/grpc_query_test.go deleted file mode 100644 index a8e83716afbf..000000000000 --- a/x/feegrant/client/rest/grpc_query_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package rest_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" - "github.com/cosmos/cosmos-sdk/x/feegrant/client/cli" - "github.com/cosmos/cosmos-sdk/x/feegrant/types" -) - -type IntegrationTestSuite struct { - suite.Suite - cfg network.Config - network *network.Network - grantee sdk.AccAddress -} - -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - - cfg := network.DefaultConfig() - - cfg.NumValidators = 1 - s.cfg = cfg - s.network = network.New(s.T(), cfg) - - val := s.network.Validators[0] - // Create new account in the keyring. - info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - newAddr := sdk.AccAddress(info.GetPubKey().Address()) - - // Send some funds to the new account. - _, err = banktestutil.MsgSendExec( - val.ClientCtx, - val.Address, - newAddr, - sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(200))), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - ) - s.Require().NoError(err) - - s.grantee = newAddr - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - -func (s *IntegrationTestSuite) TestQueryFeeAllowance() { - val := s.network.Validators[0] - baseURL := val.APIAddress - testCases := []struct { - name string - url string - expectErr bool - errorMsg string - preRun func() - postRun func(_ types.QueryFeeAllowanceResponse) - }{ - { - "fail: invalid granter", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, "invalid_granter", s.grantee.String()), - true, - "decoding bech32 failed: invalid index of 1: invalid request", - func() {}, - func(types.QueryFeeAllowanceResponse) {}, - }, - { - "fail: invalid grantee", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), "invalid_grantee"), - true, - "decoding bech32 failed: invalid index of 1: invalid request", - func() {}, - func(types.QueryFeeAllowanceResponse) {}, - }, - { - "fail: no grants", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()), - true, - "no allowance", - func() {}, - func(types.QueryFeeAllowanceResponse) {}, - }, - { - "valid query: expect single grant", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()), - false, - "", - func() { - execFeeAllowance(val, s) - }, - func(allowance types.QueryFeeAllowanceResponse) { - s.Require().Equal(allowance.FeeAllowance.Granter, val.Address.String()) - s.Require().Equal(allowance.FeeAllowance.Grantee, s.grantee.String()) - }, - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - tc.preRun() - resp, _ := rest.GetRequest(tc.url) - if tc.expectErr { - s.Require().Contains(string(resp), tc.errorMsg) - } else { - var allowance types.QueryFeeAllowanceResponse - err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance) - s.Require().NoError(err) - tc.postRun(allowance) - } - }) - } -} - -func (s *IntegrationTestSuite) TestQueryGranteeAllowances() { - val := s.network.Validators[0] - baseURL := val.APIAddress - testCases := []struct { - name string - url string - expectErr bool - errorMsg string - preRun func() - postRun func(_ types.QueryFeeAllowancesResponse) - }{ - { - "fail: invalid grantee", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, "invalid_grantee"), - true, - "decoding bech32 failed: invalid index of 1: invalid request", - func() {}, - func(types.QueryFeeAllowancesResponse) {}, - }, - { - "success: no grants", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s?pagination.offset=1", baseURL, s.grantee.String()), - false, - "", - func() {}, - func(allowances types.QueryFeeAllowancesResponse) { - s.Require().Equal(len(allowances.FeeAllowances), 0) - }, - }, - { - "valid query: expect single grant", - fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, s.grantee.String()), - false, - "", - func() { - execFeeAllowance(val, s) - }, - func(allowances types.QueryFeeAllowancesResponse) { - s.Require().Equal(len(allowances.FeeAllowances), 1) - s.Require().Equal(allowances.FeeAllowances[0].Granter, val.Address.String()) - s.Require().Equal(allowances.FeeAllowances[0].Grantee, s.grantee.String()) - }, - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - tc.preRun() - resp, _ := rest.GetRequest(tc.url) - if tc.expectErr { - s.Require().Contains(string(resp), tc.errorMsg) - } else { - var allowance types.QueryFeeAllowancesResponse - err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance) - s.Require().NoError(err) - tc.postRun(allowance) - } - }) - } -} - -func execFeeAllowance(val *network.Validator, s *IntegrationTestSuite) { - fee := sdk.NewCoin("steak", sdk.NewInt(100)) - duration := 365 * 24 * 60 * 60 - args := []string{ - val.Address.String(), - s.grantee.String(), - fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - } - - cmd := cli.NewCmdFeeGrant() - _, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - s.Require().NoError(err) -} - -func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} diff --git a/x/feegrant/client/testutil/suite.go b/x/feegrant/client/testutil/suite.go index a247ac7e8fea..3172f0a9a84b 100644 --- a/x/feegrant/client/testutil/suite.go +++ b/x/feegrant/client/testutil/suite.go @@ -3,6 +3,7 @@ package testutil import ( "fmt" "testing" + "time" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/suite" @@ -21,6 +22,12 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) +const ( + oneYear = 365 * 24 * 60 * 60 + tenHours = 10 * 60 * 60 + oneHour = 60 * 60 +) + type IntegrationTestSuite struct { suite.Suite @@ -59,7 +66,6 @@ func (s *IntegrationTestSuite) SetupSuite() { } fee := sdk.NewCoin("stake", sdk.NewInt(100)) - duration := 365 * 24 * 60 * 60 args := append( []string{ @@ -67,7 +73,7 @@ func (s *IntegrationTestSuite) SetupSuite() { grantee.String(), fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)), }, commonFlags..., ) @@ -354,7 +360,7 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(tenHours)), }, commonFlags..., ), @@ -367,9 +373,9 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { granter.String(), "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)), }, commonFlags..., ), @@ -382,10 +388,10 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { granter.String(), "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl", fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)), }, commonFlags..., ), @@ -398,10 +404,10 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { granter.String(), "cosmos1w55kgcf3ltaqdy4ww49nge3klxmrdavrr6frmp", fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(tenHours)), }, commonFlags..., ), @@ -413,10 +419,10 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { []string{ granter.String(), "cosmos1vevyks8pthkscvgazc97qyfjt40m6g9xe85ry8", - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(tenHours)), }, commonFlags..., ), @@ -429,7 +435,7 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { granter.String(), "cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8", fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), }, @@ -443,7 +449,7 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { []string{ granter.String(), "cosmos12nyk4pcf4arshznkpz882e4l4ts0lt0ap8ce54", - fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60), + fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour), fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), }, @@ -451,6 +457,21 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { ), false, 0, &sdk.TxResponse{}, }, + { + "invalid expiration", + append( + []string{ + granter.String(), + "cosmos1vevyks8pthkscvgazc97qyfjt40m6g9xe85ry8", + fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour), + fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, "invalid"), + }, + commonFlags..., + ), + true, 0, nil, + }, } for _, tc := range testCases { @@ -579,7 +600,6 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() { } fee := sdk.NewCoin("stake", sdk.NewInt(100)) - duration := 365 * 24 * 60 * 60 args := append( []string{ @@ -587,7 +607,7 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() { grantee.String(), fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()), fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), - fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration), + fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)), }, commonFlags..., ) @@ -786,3 +806,7 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() { }) } } + +func getFormattedExpiration(duration int64) string { + return time.Now().Add(time.Duration(duration) * time.Second).Format(time.RFC3339) +} diff --git a/x/feegrant/genesis.go b/x/feegrant/genesis.go index 5fd23608fcdc..22d4a2012182 100644 --- a/x/feegrant/genesis.go +++ b/x/feegrant/genesis.go @@ -6,46 +6,29 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) -// GenesisState contains a set of fee allowances, persisted from the store -type GenesisState []types.FeeAllowanceGrant - -// ValidateBasic ensures all grants in the genesis state are valid -func (g GenesisState) ValidateBasic() error { - for _, f := range g { - grant, err := f.GetFeeGrant() - if err != nil { - return err - } - err = grant.ValidateBasic() - if err != nil { - return err - } - } - return nil -} - // InitGenesis will initialize the keeper from a *previously validated* GenesisState -func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) { +func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) error { for _, f := range data.FeeAllowances { granter, err := sdk.AccAddressFromBech32(f.Granter) if err != nil { - panic(err) + return err } grantee, err := sdk.AccAddressFromBech32(f.Grantee) if err != nil { - panic(err) + return err } grant, err := f.GetFeeGrant() if err != nil { - panic(err) + return err } err = k.GrantFeeAllowance(ctx, granter, grantee, grant) if err != nil { - panic(err) + return err } } + return nil } // ExportGenesis will dump the contents of the keeper into a serializable GenesisState diff --git a/x/feegrant/genesis_test.go b/x/feegrant/genesis_test.go index 3ea58441a8a1..0088d3822b15 100644 --- a/x/feegrant/genesis_test.go +++ b/x/feegrant/genesis_test.go @@ -6,8 +6,10 @@ import ( "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" feegrant "github.com/cosmos/cosmos-sdk/x/feegrant" "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" @@ -46,12 +48,60 @@ func (suite *GenesisTestSuite) TestImportExportGenesis() { suite.Require().NoError(err) // Clear keeper suite.keeper.RevokeFeeAllowance(suite.ctx, granterAddr, granteeAddr) - feegrant.InitGenesis(suite.ctx, suite.keeper, genesis) + err = feegrant.InitGenesis(suite.ctx, suite.keeper, genesis) + suite.Require().NoError(err) newGenesis, err := feegrant.ExportGenesis(suite.ctx, suite.keeper) suite.Require().NoError(err) suite.Require().Equal(genesis, newGenesis) } +func (suite *GenesisTestSuite) TestInitGenesis() { + any, err := codectypes.NewAnyWithValue(&testdata.Dog{}) + suite.Require().NoError(err) + + testCases := []struct { + name string + feeAllowances []types.FeeAllowanceGrant + }{ + { + "invalid granter", + []types.FeeAllowanceGrant{ + { + Granter: "invalid granter", + Grantee: granteeAddr.String(), + }, + }, + }, + { + "invalid grantee", + []types.FeeAllowanceGrant{ + { + Granter: granterAddr.String(), + Grantee: "invalid grantee", + }, + }, + }, + { + "invalid allowance", + []types.FeeAllowanceGrant{ + { + Granter: granterAddr.String(), + Grantee: granteeAddr.String(), + Allowance: any, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + err := feegrant.InitGenesis(suite.ctx, suite.keeper, &types.GenesisState{FeeAllowances: tc.feeAllowances}) + suite.Require().Error(err) + }) + } +} + func TestGenesisTestSuite(t *testing.T) { suite.Run(t, new(GenesisTestSuite)) } diff --git a/x/feegrant/keeper/grpc_query_test.go b/x/feegrant/keeper/grpc_query_test.go new file mode 100644 index 000000000000..e5d02247e3a1 --- /dev/null +++ b/x/feegrant/keeper/grpc_query_test.go @@ -0,0 +1,162 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/types" +) + +func (suite *KeeperTestSuite) TestFeeAllowance() { + ctx := suite.ctx + k := suite.app.FeeGrantKeeper + + testCases := []struct { + name string + req *types.QueryFeeAllowanceRequest + expectErr bool + preRun func() + postRun func(_ *types.QueryFeeAllowanceResponse) + }{ + { + "nil request", + nil, + true, + func() {}, + func(*types.QueryFeeAllowanceResponse) {}, + }, + { + "fail: invalid granter", + &types.QueryFeeAllowanceRequest{ + Granter: "invalid_granter", + Grantee: suite.addrs[0].String(), + }, + true, + func() {}, + func(*types.QueryFeeAllowanceResponse) {}, + }, + { + "fail: invalid grantee", + &types.QueryFeeAllowanceRequest{ + Granter: suite.addrs[0].String(), + Grantee: "invalid_grantee", + }, + true, + func() {}, + func(*types.QueryFeeAllowanceResponse) {}, + }, + { + "fail: no grants", + &types.QueryFeeAllowanceRequest{ + Granter: suite.addrs[0].String(), + Grantee: suite.addrs[1].String(), + }, + true, + func() {}, + func(*types.QueryFeeAllowanceResponse) {}, + }, + { + "valid query: expect single grant", + &types.QueryFeeAllowanceRequest{ + Granter: suite.addrs[0].String(), + Grantee: suite.addrs[1].String(), + }, + false, + func() { + grantFeeAllowance(suite) + }, + func(allowance *types.QueryFeeAllowanceResponse) { + suite.Require().Equal(allowance.FeeAllowance.Granter, suite.addrs[0].String()) + suite.Require().Equal(allowance.FeeAllowance.Grantee, suite.addrs[1].String()) + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + tc.preRun() + resp, err := k.FeeAllowance(sdk.WrapSDKContext(ctx), tc.req) + if tc.expectErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + tc.postRun(resp) + } + }) + } +} + +func (suite *KeeperTestSuite) TestFeeAllowances() { + ctx := suite.ctx + k := suite.app.FeeGrantKeeper + + testCases := []struct { + name string + req *types.QueryFeeAllowancesRequest + expectErr bool + preRun func() + postRun func(_ *types.QueryFeeAllowancesResponse) + }{ + { + "nil request", + nil, + true, + func() {}, + func(*types.QueryFeeAllowancesResponse) {}, + }, + { + "fail: invalid grantee", + &types.QueryFeeAllowancesRequest{ + Grantee: "invalid_grantee", + }, + true, + func() {}, + func(*types.QueryFeeAllowancesResponse) {}, + }, + { + "no grants", + &types.QueryFeeAllowancesRequest{ + Grantee: suite.addrs[1].String(), + }, + false, + func() {}, + func(resp *types.QueryFeeAllowancesResponse) { + suite.Require().Equal(len(resp.FeeAllowances), 0) + }, + }, + { + "valid query: expect single grant", + &types.QueryFeeAllowancesRequest{ + Grantee: suite.addrs[1].String(), + }, + false, + func() { + grantFeeAllowance(suite) + }, + func(resp *types.QueryFeeAllowancesResponse) { + suite.Require().Equal(len(resp.FeeAllowances), 1) + suite.Require().Equal(resp.FeeAllowances[0].Granter, suite.addrs[0].String()) + suite.Require().Equal(resp.FeeAllowances[0].Grantee, suite.addrs[1].String()) + }, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + tc.preRun() + resp, err := k.FeeAllowances(sdk.WrapSDKContext(ctx), tc.req) + if tc.expectErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + tc.postRun(resp) + } + }) + } +} + +func grantFeeAllowance(suite *KeeperTestSuite) { + err := suite.app.FeeGrantKeeper.GrantFeeAllowance(suite.ctx, suite.addrs[0], suite.addrs[1], &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 555)), + Expiration: types.ExpiresAtHeight(334455), + }) + suite.Require().NoError(err) +} diff --git a/x/feegrant/module.go b/x/feegrant/module.go index a6f1f4f62000..77b113b1b0b7 100644 --- a/x/feegrant/module.go +++ b/x/feegrant/module.go @@ -152,7 +152,10 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, bz jso var gs types.GenesisState cdc.MustUnmarshalJSON(bz, &gs) - InitGenesis(ctx, am.keeper, &gs) + err := InitGenesis(ctx, am.keeper, &gs) + if err != nil { + panic(err) + } return []abci.ValidatorUpdate{} }