diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index 44df6f5e9..1b74e120f 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 c355ca4b5..2d38cd053 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 a8e83716a..000000000 --- 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 a247ac7e8..3172f0a9a 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 5fd23608f..22d4a2012 100644 --- a/x/feegrant/genesis.go +++ b/x/feegrant/genesis.go @@ -6,17 +6,24 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) -// GenesisState contains a set of fee allowances, persisted from the store -type GenesisState []types.FeeAllowanceGrant +// InitGenesis will initialize the keeper from a *previously validated* 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 { + return err + } + grantee, err := sdk.AccAddressFromBech32(f.Grantee) + if err != nil { + return err + } -// 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() + + err = k.GrantFeeAllowance(ctx, granter, grantee, grant) if err != nil { return err } @@ -24,30 +31,6 @@ func (g GenesisState) ValidateBasic() error { return nil } -// InitGenesis will initialize the keeper from a *previously validated* GenesisState -func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) { - for _, f := range data.FeeAllowances { - granter, err := sdk.AccAddressFromBech32(f.Granter) - if err != nil { - panic(err) - } - grantee, err := sdk.AccAddressFromBech32(f.Grantee) - if err != nil { - panic(err) - } - - grant, err := f.GetFeeGrant() - if err != nil { - panic(err) - } - - err = k.GrantFeeAllowance(ctx, granter, grantee, grant) - if err != nil { - panic(err) - } - } -} - // ExportGenesis will dump the contents of the keeper into a serializable GenesisState // // All expiration heights will be thrown off if we dump state and start at a new diff --git a/x/feegrant/genesis_test.go b/x/feegrant/genesis_test.go index 3ea58441a..0088d3822 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 000000000..e5d02247e --- /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 a6f1f4f62..77b113b1b 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{} }