feat: feegrant cli mocks (#13068)

* add mocks

* review changes

* fix tests

* review changes

* review changes

* review

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
atheeshp 2022-09-07 22:32:59 +05:30 committed by GitHub
parent f10bf6a4dc
commit 1756c19cd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 1988 additions and 3 deletions

View File

@ -1,14 +1,13 @@
//go:build e2e
// +build e2e
package testutil
package feegrant
import (
"testing"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/network"
clienttestutil "github.com/cosmos/cosmos-sdk/x/feegrant/client/testutil"
"github.com/stretchr/testify/suite"
)
@ -16,5 +15,5 @@ import (
func TestIntegrationTestSuite(t *testing.T) {
cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
cfg.NumValidators = 3
suite.Run(t, clienttestutil.NewIntegrationTestSuite(cfg))
suite.Run(t, NewIntegrationTestSuite(cfg))
}

992
tests/e2e/feegrant/suite.go Normal file
View File

@ -0,0 +1,992 @@
package feegrant
import (
"fmt"
"strings"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
tmcli "github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/testutil"
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/x/feegrant"
"github.com/cosmos/cosmos-sdk/x/feegrant/client/cli"
govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
)
const (
oneYear = 365 * 24 * 60 * 60
tenHours = 10 * 60 * 60
oneHour = 60 * 60
)
type IntegrationTestSuite struct {
suite.Suite
cfg network.Config
network *network.Network
addedGranter sdk.AccAddress
addedGrantee sdk.AccAddress
addedGrant feegrant.Grant
}
func NewIntegrationTestSuite(cfg network.Config) *IntegrationTestSuite {
return &IntegrationTestSuite{cfg: cfg}
}
func (s *IntegrationTestSuite) SetupSuite() {
s.T().Log("setting up integration test suite")
if testing.Short() {
s.T().Skip("skipping test in unit-tests mode.")
}
var err error
s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg)
s.Require().NoError(err)
_, err = s.network.WaitForHeight(1)
s.Require().NoError(err)
val := s.network.Validators[0]
granter := val.Address
grantee := s.network.Validators[1].Address
s.createGrant(granter, grantee)
grant, err := feegrant.NewGrant(granter, grantee, &feegrant.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
})
s.Require().NoError(err)
s.addedGrant = grant
s.addedGranter = granter
s.addedGrantee = grantee
}
// createGrant creates a new basic allowance fee grant from granter to grantee.
func (s *IntegrationTestSuite) createGrant(granter, grantee sdk.Address) {
val := s.network.Validators[0]
clientCtx := val.ClientCtx
commonFlags := []string{
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(100))).String()),
}
fee := sdk.NewCoin("stake", sdk.NewInt(100))
args := append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
_, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(err)
_, 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) TestCmdGetFeeGrant() {
val := s.network.Validators[0]
granter := val.Address
grantee := s.addedGrantee
clientCtx := val.ClientCtx
testCases := []struct {
name string
args []string
expectErrMsg string
expectErr bool
respType *feegrant.Grant
resp *feegrant.Grant
}{
{
"wrong granter",
[]string{
"wrong_granter",
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"decoding bech32 failed",
true, nil, nil,
},
{
"wrong grantee",
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"decoding bech32 failed",
true, nil, nil,
},
{
"non existed grant",
[]string{
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"fee-grant not found",
true, nil, nil,
},
{
"valid req",
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"",
false,
&feegrant.Grant{},
&s.addedGrant,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expectErrMsg)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
s.Require().Equal(tc.respType.Grantee, tc.respType.Grantee)
s.Require().Equal(tc.respType.Granter, tc.respType.Granter)
grant, err := tc.respType.GetGrant()
s.Require().NoError(err)
grant1, err1 := tc.resp.GetGrant()
s.Require().NoError(err1)
s.Require().Equal(
grant.(*feegrant.BasicAllowance).SpendLimit,
grant1.(*feegrant.BasicAllowance).SpendLimit,
)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdGetFeeGrantsByGrantee() {
val := s.network.Validators[0]
grantee := s.addedGrantee
clientCtx := val.ClientCtx
testCases := []struct {
name string
args []string
expectErr bool
resp *feegrant.QueryAllowancesResponse
expectLength int
}{
{
"wrong grantee",
[]string{
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, nil, 0,
},
{
"non existent grantee",
[]string{
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesResponse{}, 0,
},
{
"valid req",
[]string{
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesResponse{}, 1,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrantsByGrantee()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.resp), out.String())
s.Require().Len(tc.resp.Allowances, tc.expectLength)
}
})
}
}
func (s *IntegrationTestSuite) TestCmdGetFeeGrantsByGranter() {
val := s.network.Validators[0]
granter := s.addedGranter
clientCtx := val.ClientCtx
testCases := []struct {
name string
args []string
expectErr bool
resp *feegrant.QueryAllowancesByGranterResponse
expectLength int
}{
{
"wrong grantee",
[]string{
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, nil, 0,
},
{
"non existent grantee",
[]string{
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesByGranterResponse{}, 0,
},
{
"valid req",
[]string{
granter.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesByGranterResponse{}, 1,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrantsByGranter()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.resp), out.String())
s.Require().Len(tc.resp.Allowances, tc.expectLength)
}
})
}
}
func (s *IntegrationTestSuite) TestNewCmdFeeGrant() {
val := s.network.Validators[0]
granter := val.Address
alreadyExistedGrantee := s.addedGrantee
clientCtx := val.ClientCtx
fromAddr, fromName, _, err := client.GetFromFields(clientCtx, clientCtx.Keyring, granter.String())
s.Require().Equal(fromAddr, granter)
s.Require().NoError(err)
commonFlags := []string{
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()),
}
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
{
"wrong granter address",
append(
[]string{
"wrong_granter",
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"wrong grantee address",
append(
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"wrong granter key name",
append(
[]string{
"invalid_granter",
"cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"valid basic fee grant",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant with granter key name",
append(
[]string{
fromName,
"cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, fromName),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant with amino",
append(
[]string{
granter.String(),
"cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without spend limit",
append(
[]string{
granter.String(),
"cosmos17h5lzptx3ghvsuhk7wx4c4hnl7rsswxjer97em",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without expiration",
append(
[]string{
granter.String(),
"cosmos16dlc38dcqt0uralyd8hksxyrny6kaeqfjvjwp5",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without spend-limit and expiration",
append(
[]string{
granter.String(),
"cosmos1ku40qup9vwag4wtf8cls9mkszxfthaklxkp3c8",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"try to add existed grant",
append(
[]string{
granter.String(),
alreadyExistedGrantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 18, &sdk.TxResponse{},
},
{
"invalid number of args(periodic fee grant)",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
true, 0, nil,
},
{
"period mentioned and period limit omitted, invalid periodic grant",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)),
},
commonFlags...,
),
true, 0, nil,
},
{
"period cannot be greater than the actual expiration(periodic fee grant)",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)),
},
commonFlags...,
),
true, 0, nil,
},
{
"valid periodic fee grant",
append(
[]string{
granter.String(),
"cosmos1w55kgcf3ltaqdy4ww49nge3klxmrdavrr6frmp",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
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, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without spend-limit",
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, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without expiration",
append(
[]string{
granter.String(),
"cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without spend-limit and expiration",
append(
[]string{
granter.String(),
"cosmos12nyk4pcf4arshznkpz882e4l4ts0lt0ap8ce54",
fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
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 {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
}
func (s *IntegrationTestSuite) TestNewCmdRevokeFeegrant() {
val := s.network.Validators[0]
granter := s.addedGranter
grantee := s.addedGrantee
clientCtx := val.ClientCtx
commonFlags := []string{
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()),
}
// Create new fee grant specifically to test amino.
aminoGrantee, err := sdk.AccAddressFromBech32("cosmos16ydaqh0fcnh4qt7a3jme4mmztm2qel5axcpw00")
s.Require().NoError(err)
s.createGrant(granter, aminoGrantee)
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
{
"invalid grantee",
append(
[]string{
"wrong_granter",
grantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"invalid grantee",
append(
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"Non existed grant",
append(
[]string{
granter.String(),
"cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 38, &sdk.TxResponse{},
},
{
"Valid revoke",
append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"Valid revoke with amino",
append(
[]string{
granter.String(),
aminoGrantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdRevokeFeegrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
}
func (s *IntegrationTestSuite) TestTxWithFeeGrant() {
s.T().Skip() // TODO to re-enable in #12274
val := s.network.Validators[0]
clientCtx := val.ClientCtx
granter := val.Address
// creating an account manually (This account won't be exist in state)
k, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
pub, err := k.GetPubKey()
s.Require().NoError(err)
grantee := sdk.AccAddress(pub.Address())
commonFlags := []string{
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()),
}
fee := sdk.NewCoin("stake", sdk.NewInt(100))
args := append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(err)
_, err = s.network.WaitForHeight(1)
s.Require().NoError(err)
testcases := []struct {
name string
from string
flags []string
expErrCode uint32
}{
{
name: "granted fee allowance for an account which is not in state and creating any tx with it by using --fee-granter shouldn't fail",
from: grantee.String(),
flags: []string{fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String())},
},
{
name: "--fee-payer should also sign the tx (direct)",
from: grantee.String(),
flags: []string{fmt.Sprintf("--%s=%s", flags.FlagFeePayer, granter.String())},
expErrCode: 4,
},
{
name: "--fee-payer should also sign the tx (amino-json)",
from: grantee.String(),
flags: []string{
fmt.Sprintf("--%s=%s", flags.FlagFeePayer, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
expErrCode: 4,
},
{
name: "use --fee-payer and --fee-granter together works",
from: grantee.String(),
flags: []string{
fmt.Sprintf("--%s=%s", flags.FlagFeePayer, grantee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
},
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
out, err := govtestutil.MsgSubmitLegacyProposal(val.ClientCtx, tc.from,
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
tc.flags...,
)
s.Require().NoError(err)
var resp sdk.TxResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
s.Require().Equal(tc.expErrCode, resp.Code, resp)
})
}
}
func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
s.T().Skip() // TODO to re-enable in #12274
val := s.network.Validators[0]
granter := val.Address
k, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
pub, err := k.GetPubKey()
s.Require().NoError(err)
grantee := sdk.AccAddress(pub.Address())
clientCtx := val.ClientCtx
commonFlags := []string{
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(100))).String()),
}
spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))
allowMsgs := strings.Join([]string{sdk.MsgTypeURL(&govv1beta1.MsgSubmitProposal{}), sdk.MsgTypeURL(&govv1.MsgVoteWeighted{})}, ",")
testCases := []struct {
name string
args []string
expectErr bool
respType proto.Message
expectedCode uint32
}{
{
"invalid granter address",
append(
[]string{
"not an address",
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, &sdk.TxResponse{}, 0,
},
{
"invalid grantee address",
append(
[]string{
granter.String(),
"not an address",
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, &sdk.TxResponse{}, 0,
},
{
"valid filter fee grant",
append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, &sdk.TxResponse{}, 0,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
args := []string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
}
// get filtered fee allowance and check info
cmd := cli.GetCmdQueryFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(err)
resp := &feegrant.Grant{}
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp), out.String())
s.Require().Equal(resp.Grantee, resp.Grantee)
s.Require().Equal(resp.Granter, resp.Granter)
grant, err := resp.GetGrant()
s.Require().NoError(err)
filteredFeeGrant, err := grant.(*feegrant.AllowedMsgAllowance).GetAllowance()
s.Require().NoError(err)
s.Require().Equal(
filteredFeeGrant.(*feegrant.BasicAllowance).SpendLimit.String(),
spendLimit.String(),
)
// exec filtered fee allowance
cases := []struct {
name string
malleate func() (testutil.BufferWriter, error)
respType proto.Message
expectedCode uint32
}{
{
"valid proposal tx",
func() (testutil.BufferWriter, error) {
return govtestutil.MsgSubmitLegacyProposal(val.ClientCtx, grantee.String(),
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(100))).String()),
)
},
&sdk.TxResponse{},
0,
},
{
"valid weighted_vote tx",
func() (testutil.BufferWriter, error) {
return govtestutil.MsgVote(val.ClientCtx, grantee.String(), "0", "yes",
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(100))).String()),
)
},
&sdk.TxResponse{},
2,
},
{
"should fail with unauthorized msgs",
func() (testutil.BufferWriter, error) {
args := append(
[]string{
grantee.String(),
"cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
return clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
},
&sdk.TxResponse{},
7,
},
}
for _, tc := range cases {
tc := tc
s.Run(tc.name, func() {
out, err := tc.malleate()
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
})
}
}
func getFormattedExpiration(duration int64) string {
return time.Now().Add(time.Duration(duration) * time.Second).Format(time.RFC3339)
}

View File

@ -0,0 +1,155 @@
package cli_test
import (
"fmt"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/x/feegrant"
"github.com/cosmos/cosmos-sdk/x/feegrant/client/cli"
tmcli "github.com/tendermint/tendermint/libs/cli"
)
func (s *CLITestSuite) TestCmdGetFeeGrant() {
granter := s.addedGranter
grantee := s.addedGrantee
testCases := []struct {
name string
args []string
expectErrMsg string
expectErr bool
respType *feegrant.QueryAllowanceResponse
resp *feegrant.Grant
}{
{
"wrong granter",
[]string{
"wrong_granter",
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"decoding bech32 failed",
true, nil, nil,
},
{
"wrong grantee",
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"decoding bech32 failed",
true, nil, nil,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrant()
out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expectErrMsg)
} else {
s.Require().NoError(err)
s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
}
})
}
}
func (s *CLITestSuite) TestCmdGetFeeGrantsByGrantee() {
grantee := s.addedGrantee
clientCtx := s.clientCtx
testCases := []struct {
name string
args []string
expectErr bool
resp *feegrant.QueryAllowancesResponse
expectLength int
}{
{
"wrong grantee",
[]string{
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, nil, 0,
},
{
"valid req",
[]string{
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesResponse{}, 1,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrantsByGrantee()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.resp), out.String())
}
})
}
}
func (s *CLITestSuite) TestCmdGetFeeGrantsByGranter() {
granter := s.addedGranter
clientCtx := s.clientCtx
testCases := []struct {
name string
args []string
expectErr bool
resp *feegrant.QueryAllowancesByGranterResponse
expectLength int
}{
{
"wrong grantee",
[]string{
"wrong_grantee",
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
true, nil, 0,
},
{
"valid req",
[]string{
granter.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
false, &feegrant.QueryAllowancesByGranterResponse{}, 1,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.GetCmdQueryFeeGrantsByGranter()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.resp), out.String())
}
})
}
}

View File

@ -0,0 +1,839 @@
package cli_test
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmlibs "github.com/tendermint/tendermint/libs/bytes"
rpcclient "github.com/tendermint/tendermint/rpc/client"
rpcclientmock "github.com/tendermint/tendermint/rpc/client/mock"
coretypes "github.com/tendermint/tendermint/rpc/core/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/feegrant"
"github.com/cosmos/cosmos-sdk/x/feegrant/client/cli"
"github.com/cosmos/cosmos-sdk/x/feegrant/module"
govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
)
const (
oneYear = 365 * 24 * 60 * 60
tenHours = 10 * 60 * 60
oneHour = 60 * 60
)
type CLITestSuite struct {
suite.Suite
addedGranter sdk.AccAddress
addedGrantee sdk.AccAddress
addedGrant feegrant.Grant
kr keyring.Keyring
baseCtx client.Context
encCfg testutilmod.TestEncodingConfig
clientCtx client.Context
accounts []sdk.AccAddress
}
var _ client.TendermintRPC = (*mockTendermintRPC)(nil)
type mockTendermintRPC struct {
rpcclientmock.Client
responseQuery abci.ResponseQuery
}
func newMockTendermintRPC(respQuery abci.ResponseQuery) mockTendermintRPC {
return mockTendermintRPC{responseQuery: respQuery}
}
func (m mockTendermintRPC) ABCIQueryWithOptions(
_ context.Context,
_ string, _ tmlibs.HexBytes,
_ rpcclient.ABCIQueryOptions,
) (*coretypes.ResultABCIQuery, error) {
return &coretypes.ResultABCIQuery{Response: m.responseQuery}, nil
}
func (m mockTendermintRPC) BroadcastTxSync(context.Context, tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) {
return &coretypes.ResultBroadcastTx{Code: 0}, nil
}
func TestCLITestSuite(t *testing.T) {
suite.Run(t, new(CLITestSuite))
}
func (s *CLITestSuite) SetupSuite() {
s.T().Log("setting up integration test suite")
s.encCfg = testutilmod.MakeTestEncodingConfig(module.AppModuleBasic{})
s.kr = keyring.NewInMemory(s.encCfg.Codec)
s.baseCtx = client.Context{}.
WithKeyring(s.kr).
WithTxConfig(s.encCfg.TxConfig).
WithCodec(s.encCfg.Codec).
WithClient(mockTendermintRPC{Client: rpcclientmock.Client{}}).
WithAccountRetriever(client.MockAccountRetriever{}).
WithOutput(io.Discard).
WithChainID("test-chain")
var outBuf bytes.Buffer
ctxGen := func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{})
c := newMockTendermintRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
}
s.clientCtx = ctxGen().WithOutput(&outBuf)
if testing.Short() {
s.T().Skip("skipping test in unit-tests mode.")
}
accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 2)
granter := accounts[0].Address
grantee := accounts[1].Address
s.createGrant(granter, grantee)
grant, err := feegrant.NewGrant(granter, grantee, &feegrant.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))),
})
s.Require().NoError(err)
s.addedGrant = grant
s.addedGranter = granter
s.addedGrantee = grantee
for _, v := range accounts {
s.accounts = append(s.accounts, v.Address)
}
s.accounts[1] = accounts[1].Address
}
// createGrant creates a new basic allowance fee grant from granter to grantee.
func (s *CLITestSuite) createGrant(granter, grantee sdk.Address) {
commonFlags := []string{
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100))).String()),
}
fee := sdk.NewCoin("stake", sdk.NewInt(100))
args := append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, args)
s.Require().NoError(err)
var resp sdk.TxResponse
s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
s.Require().Equal(resp.Code, uint32(0))
}
func (s *CLITestSuite) TestNewCmdFeeGrant() {
granter := s.accounts[0]
alreadyExistedGrantee := s.addedGrantee
clientCtx := s.clientCtx
fromAddr, fromName, _, err := client.GetFromFields(s.baseCtx, s.kr, granter.String())
s.Require().Equal(fromAddr, granter)
s.Require().NoError(err)
commonFlags := []string{
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()),
}
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
{
"wrong granter address",
append(
[]string{
"wrong_granter",
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"wrong grantee address",
append(
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"wrong granter key name",
append(
[]string{
"invalid_granter",
"cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"valid basic fee grant",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant with granter key name",
append(
[]string{
fromName,
"cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, fromName),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant with amino",
append(
[]string{
granter.String(),
"cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without spend limit",
append(
[]string{
granter.String(),
"cosmos17h5lzptx3ghvsuhk7wx4c4hnl7rsswxjer97em",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without expiration",
append(
[]string{
granter.String(),
"cosmos16dlc38dcqt0uralyd8hksxyrny6kaeqfjvjwp5",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid basic fee grant without spend-limit and expiration",
append(
[]string{
granter.String(),
"cosmos1ku40qup9vwag4wtf8cls9mkszxfthaklxkp3c8",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"try to add existed grant",
append(
[]string{
granter.String(),
alreadyExistedGrantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 18, &sdk.TxResponse{},
},
{
"invalid number of args(periodic fee grant)",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
true, 0, nil,
},
{
"period mentioned and period limit omitted, invalid periodic grant",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)),
},
commonFlags...,
),
true, 0, nil,
},
{
"period cannot be greater than the actual expiration(periodic fee grant)",
append(
[]string{
granter.String(),
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, tenHours),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneHour)),
},
commonFlags...,
),
true, 0, nil,
},
{
"valid periodic fee grant",
append(
[]string{
granter.String(),
"cosmos1w55kgcf3ltaqdy4ww49nge3klxmrdavrr6frmp",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
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, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without spend-limit",
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, getFormattedExpiration(tenHours)),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without expiration",
append(
[]string{
granter.String(),
"cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"valid periodic fee grant without spend-limit and expiration",
append(
[]string{
granter.String(),
"cosmos12nyk4pcf4arshznkpz882e4l4ts0lt0ap8ce54",
fmt.Sprintf("--%s=%d", cli.FlagPeriod, oneHour),
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
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 {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
}
})
}
}
func (s *CLITestSuite) TestNewCmdRevokeFeegrant() {
granter := s.addedGranter
grantee := s.addedGrantee
clientCtx := s.clientCtx
commonFlags := []string{
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()),
}
// Create new fee grant specifically to test amino.
aminoGrantee, err := sdk.AccAddressFromBech32("cosmos16ydaqh0fcnh4qt7a3jme4mmztm2qel5axcpw00")
s.Require().NoError(err)
s.createGrant(granter, aminoGrantee)
testCases := []struct {
name string
args []string
expectErr bool
expectedCode uint32
respType proto.Message
}{
{
"invalid grantee",
append(
[]string{
"wrong_granter",
grantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"invalid grantee",
append(
[]string{
granter.String(),
"wrong_grantee",
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, 0, nil,
},
{
"Valid revoke",
append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
{
"Valid revoke with amino",
append(
[]string{
granter.String(),
aminoGrantee.String(),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
commonFlags...,
),
false, 0, &sdk.TxResponse{},
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdRevokeFeegrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
}
})
}
}
func (s *CLITestSuite) TestTxWithFeeGrant() {
// s.T().Skip() // TODO to re-enable in #12274
clientCtx := s.clientCtx
granter := s.addedGranter
// creating an account manually (This account won't be exist in state)
k, _, err := s.baseCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
pub, err := k.GetPubKey()
s.Require().NoError(err)
grantee := sdk.AccAddress(pub.Address())
commonFlags := []string{
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()),
}
fee := sdk.NewCoin("stake", sdk.NewInt(100))
args := append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
fmt.Sprintf("--%s=%s", cli.FlagExpiration, getFormattedExpiration(oneYear)),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
var res sdk.TxResponse
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String())
testcases := []struct {
name string
from string
flags []string
expErrCode uint32
}{
{
name: "granted fee allowance for an account which is not in state and creating any tx with it by using --fee-granter shouldn't fail",
from: grantee.String(),
flags: []string{fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String())},
},
{
name: "--fee-payer should also sign the tx (direct)",
from: grantee.String(),
flags: []string{fmt.Sprintf("--%s=%s", flags.FlagFeePayer, granter.String())},
expErrCode: 4,
},
{
name: "--fee-payer should also sign the tx (amino-json)",
from: grantee.String(),
flags: []string{
fmt.Sprintf("--%s=%s", flags.FlagFeePayer, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
},
expErrCode: 4,
},
{
name: "use --fee-payer and --fee-granter together works",
from: grantee.String(),
flags: []string{
fmt.Sprintf("--%s=%s", flags.FlagFeePayer, grantee.String()),
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
},
},
}
for _, tc := range testcases {
s.Run(tc.name, func() {
err := s.msgSubmitLegacyProposal(s.baseCtx, tc.from,
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
tc.flags...,
)
s.Require().NoError(err)
var resp sdk.TxResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
})
}
}
func (s *CLITestSuite) msgSubmitLegacyProposal(clientCtx client.Context, from, title, description, proposalType string, extraArgs ...string) error {
var commonArgs = []string{
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
}
args := append([]string{
fmt.Sprintf("--%s=%s", govcli.FlagTitle, title),
fmt.Sprintf("--%s=%s", govcli.FlagDescription, description),
fmt.Sprintf("--%s=%s", govcli.FlagProposalType, proposalType),
fmt.Sprintf("--%s=%s", flags.FlagFrom, from),
}, commonArgs...)
args = append(args, extraArgs...)
cmd := govcli.NewCmdSubmitLegacyProposal()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(err)
var resp sdk.TxResponse
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
return err
}
func (s *CLITestSuite) TestFilteredFeeAllowance() {
granter := s.addedGranter
k, _, err := s.baseCtx.Keyring.NewMnemonic("grantee1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
pub, err := k.GetPubKey()
s.Require().NoError(err)
grantee := sdk.AccAddress(pub.Address())
clientCtx := s.clientCtx
commonFlags := []string{
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))).String()),
}
spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))
allowMsgs := strings.Join([]string{sdk.MsgTypeURL(&govv1beta1.MsgSubmitProposal{}), sdk.MsgTypeURL(&govv1.MsgVoteWeighted{})}, ",")
testCases := []struct {
name string
args []string
expectErr bool
respType proto.Message
expectedCode uint32
}{
{
"invalid granter address",
append(
[]string{
"not an address",
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, &sdk.TxResponse{}, 0,
},
{
"invalid grantee address",
append(
[]string{
granter.String(),
"not an address",
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
true, &sdk.TxResponse{}, 0,
},
{
"valid filter fee grant",
append(
[]string{
granter.String(),
grantee.String(),
fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
},
commonFlags...,
),
false, &sdk.TxResponse{}, 0,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
}
})
}
// exec filtered fee allowance
cases := []struct {
name string
malleate func() error
respType proto.Message
expectedCode uint32
}{
{
"valid proposal tx",
func() error {
return s.msgSubmitLegacyProposal(s.baseCtx, grantee.String(),
"Text Proposal", "No desc", govv1beta1.ProposalTypeText,
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))).String()),
)
},
&sdk.TxResponse{},
0,
},
{
"valid weighted_vote tx",
func() error {
return s.msgVote(s.baseCtx, grantee.String(), "0", "yes",
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter.String()),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))).String()),
)
},
&sdk.TxResponse{},
2,
},
{
"should fail with unauthorized msgs",
func() error {
args := append(
[]string{
grantee.String(),
"cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
fmt.Sprintf("--%s=%s", flags.FlagFeeGranter, granter),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &sdk.TxResponse{}), out.String())
return err
},
&sdk.TxResponse{},
7,
},
}
for _, tc := range cases {
tc := tc
s.Run(tc.name, func() {
err := tc.malleate()
s.Require().NoError(err)
})
}
}
// msgVote votes for a proposal
func (s *CLITestSuite) msgVote(clientCtx client.Context, from, id, vote string, extraArgs ...string) error {
var commonArgs = []string{
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()),
}
args := append([]string{
id,
vote,
fmt.Sprintf("--%s=%s", flags.FlagFrom, from),
}, commonArgs...)
args = append(args, extraArgs...)
cmd := govcli.NewCmdWeightedVote()
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &sdk.TxResponse{}), out.String())
return err
}
func getFormattedExpiration(duration int64) string {
return time.Now().Add(time.Duration(duration) * time.Second).Format(time.RFC3339)
}