Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feegrant filtered msgs #8604

Merged
merged 64 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
4d3308e
WIP: add filtered message
atheeshp Feb 17, 2021
6ac7630
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Feb 17, 2021
936f190
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 1, 2021
ee6d860
updated `Accept` interface method
atheeshp Mar 1, 2021
1cb3161
fix tests
atheeshp Mar 1, 2021
5710622
add cli tests for filtered fee allowance
atheeshp Mar 1, 2021
1c36f98
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 1, 2021
cef8f48
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 1, 2021
b2cd491
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 1, 2021
6343436
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 2, 2021
eea5069
fix tests
atheeshp Mar 2, 2021
cd25d55
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 2, 2021
2d76e74
Merge branch 'atheesh/feegrant-filtered-msgs' of github.com:cosmos/co…
atheeshp Mar 2, 2021
2c1868a
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 3, 2021
e741b95
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 9, 2021
0ab4580
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 12, 2021
a4d53d3
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 15, 2021
eeae712
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 16, 2021
6004cf0
review changes
atheeshp Mar 16, 2021
d306fb5
rename `filteredFeeAllowance` message
atheeshp Mar 17, 2021
8696f57
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 17, 2021
a9d972d
review changes
atheeshp Mar 17, 2021
fb60801
review changes
atheeshp Mar 17, 2021
6bf5399
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 17, 2021
9fd6357
review changes
atheeshp Mar 18, 2021
fd2cc4e
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 18, 2021
8a0d0e3
Merge branch 'master' into atheesh/feegrant-filtered-msgs
Mar 18, 2021
0dae893
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 19, 2021
0b1830f
Merge branch 'atheesh/feegrant-filtered-msgs' of github.com:cosmos/co…
atheeshp Mar 19, 2021
277cc78
review changes
atheeshp Mar 19, 2021
e3e96e8
review changes
atheeshp Mar 19, 2021
47172fc
update errors
atheeshp Mar 19, 2021
a007543
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 19, 2021
722664e
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 22, 2021
0c4146b
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 22, 2021
ab04b31
remove validation
atheeshp Mar 24, 2021
9137a91
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 24, 2021
da8af9b
fix conflicts
atheeshp Mar 24, 2021
a5da1d5
add `ctx` to `Accept` method
atheeshp Mar 25, 2021
d160707
fix test
atheeshp Mar 25, 2021
97b4a84
add gas consumption
atheeshp Mar 25, 2021
c4984e4
review changes
atheeshp Mar 25, 2021
79cfc93
review changes
atheeshp Mar 25, 2021
4dd2bbd
revert
atheeshp Mar 25, 2021
a2d1b90
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 25, 2021
91ffbac
review changes
atheeshp Mar 26, 2021
ee30459
Merge branch 'atheesh/feegrant-filtered-msgs' of github.com:cosmos/co…
atheeshp Mar 26, 2021
f213a5d
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 26, 2021
d730d1d
improve error handling
atheeshp Mar 26, 2021
f77ccbb
fix test
atheeshp Mar 26, 2021
53ec16b
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 26, 2021
7f4e213
Merge branch 'master' into atheesh/feegrant-filtered-msgs
atheeshp Mar 26, 2021
3f6bd89
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 30, 2021
2373aa9
review changes
atheeshp Mar 30, 2021
5047ffe
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Mar 30, 2021
45edce5
Merge branch 'atheesh/feegrant-filtered-msgs' of github.com:cosmos/co…
atheeshp Mar 30, 2021
a46a603
Merge branch 'master' into atheesh/feegrant-filtered-msgs
sahith-narahari Apr 6, 2021
e9f399e
update gas
atheeshp Apr 8, 2021
629c319
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Apr 8, 2021
5b8806b
Merge branch 'atheesh/feegrant-filtered-msgs' of github.com:cosmos/co…
atheeshp Apr 8, 2021
1c58061
update type
atheeshp Apr 8, 2021
b5f6070
Merge branch 'master' of github.com:cosmos/cosmos-sdk into atheesh/fe…
atheeshp Apr 9, 2021
05b7f6c
review changes
atheeshp Apr 9, 2021
06a9433
Merge branch 'master' into atheesh/feegrant-filtered-msgs
mergify[bot] Apr 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@
- [Msg](#cosmos.evidence.v1beta1.Msg)

- [cosmos/feegrant/v1beta1/feegrant.proto](#cosmos/feegrant/v1beta1/feegrant.proto)
- [AllowedMsgFeeAllowance](#cosmos.feegrant.v1beta1.AllowedMsgFeeAllowance)
- [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance)
- [Duration](#cosmos.feegrant.v1beta1.Duration)
- [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt)
Expand Down Expand Up @@ -4037,6 +4038,22 @@ Msg defines the evidence Msg service.



<a name="cosmos.feegrant.v1beta1.AllowedMsgFeeAllowance"></a>

### AllowedMsgFeeAllowance
AllowedMsgFeeAllowance creates allowance only for specified message types.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | allowance can be any of basic and filtered fee allowance. |
| `allowed_messages` | [string](#string) | repeated | allowed_messages are the messages for which the grantee has the access. |






<a name="cosmos.feegrant.v1beta1.BasicFeeAllowance"></a>

### BasicFeeAllowance
Expand Down
12 changes: 12 additions & 0 deletions proto/cosmos/feegrant/v1beta1/feegrant.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ message PeriodicFeeAllowance {
ExpiresAt period_reset = 5 [(gogoproto.nullable) = false];
}

// AllowedMsgFeeAllowance creates allowance only for specified message types.
message AllowedMsgFeeAllowance {
option (gogoproto.goproto_getters) = false;
option (cosmos_proto.implements_interface) = "FeeAllowanceI";

// allowance can be any of basic and filtered fee allowance.
google.protobuf.Any allowance = 1 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];

// allowed_messages are the messages for which the grantee has the access.
repeated string allowed_messages = 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense.

}

// Duration is a span of a clock time or number of blocks.
// This is designed to be added to an ExpiresAt struct.
message Duration {
Expand Down
2 changes: 1 addition & 1 deletion x/auth/ante/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ type AccountKeeper interface {

// FeegrantKeeper defines the expected feegrant keeper.
type FeegrantKeeper interface {
UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error
UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error
}
2 changes: 1 addition & 1 deletion x/auth/ante/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
if dfd.feegrantKeeper == nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled")
} else if !feeGranter.Equals(feePayer) {
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee)
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs())

if err != nil {
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
Expand Down
82 changes: 82 additions & 0 deletions x/feegrant/ante/fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package ante

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
)

// DeductGrantedFeeDecorator deducts fees from fee_payer or fee_granter (if exists a valid fee allowance) of the tx
// If the fee_payer or fee_granter does not have the funds to pay for the fees, return with InsufficientFunds error
// Call next AnteHandler if fees successfully deducted
// CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator
type DeductGrantedFeeDecorator struct {
ak types.AccountKeeper
k keeper.Keeper
bk types.BankKeeper
}

func NewDeductGrantedFeeDecorator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) DeductGrantedFeeDecorator {
return DeductGrantedFeeDecorator{
ak: ak,
k: k,
bk: bk,
}
}

// AnteHandle performs a decorated ante-handler responsible for deducting transaction
// fees. Fees will be deducted from the account designated by the FeePayer on a
// transaction by default. However, if the fee payer differs from the transaction
// signer, the handler will check if a fee grant has been authorized. If the
// transaction's signer does not exist, it will be created.
func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a GrantedFeeTx")
}

// sanity check from DeductFeeDecorator
if addr := d.ak.GetModuleAddress(authtypes.FeeCollectorName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", authtypes.FeeCollectorName))
}

fee := feeTx.GetFee()
feePayer := feeTx.FeePayer()
feeGranter := feeTx.FeeGranter()

deductFeesFrom := feePayer

// ensure the grant is allowed, if we request a different fee payer
if feeGranter != nil && !feeGranter.Equals(feePayer) {
err := d.k.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs())
if err != nil {
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
}

deductFeesFrom = feeGranter
}

// now, either way, we know that we are authorized to deduct the fees from the deductFeesFrom account
deductFeesFromAcc := d.ak.GetAccount(ctx, deductFeesFrom)
if deductFeesFromAcc == nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom)
}

// move on if there is no fee to deduct
if fee.IsZero() {
return next(ctx, tx, simulate)
}

// deduct fee if non-zero
err = authante.DeductFees(d.bk, ctx, deductFeesFromAcc, fee)
if err != nil {
return ctx, err
}

return next(ctx, tx, simulate)
}
188 changes: 184 additions & 4 deletions x/feegrant/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"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"
Expand Down Expand Up @@ -40,7 +41,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
}

cfg := network.DefaultConfig()
cfg.NumValidators = 2
cfg.NumValidators = 3

s.cfg = cfg
s.network = network.New(s.T(), cfg)
Expand Down Expand Up @@ -137,7 +138,7 @@ func (s *IntegrationTestSuite) TestCmdGetFeeGrant() {
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
"no fee allowance found",
"no allowance",
true, nil, nil,
},
{
Expand Down Expand Up @@ -169,9 +170,13 @@ func (s *IntegrationTestSuite) TestCmdGetFeeGrant() {
s.Require().NoError(clientCtx.JSONMarshaler.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.GetFeeGrant()
s.Require().NoError(err)
grant1, err1 := tc.resp.GetFeeGrant()
s.Require().NoError(err1)
s.Require().Equal(
tc.respType.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
tc.resp.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
grant.(*types.BasicFeeAllowance).SpendLimit,
grant1.(*types.BasicFeeAllowance).SpendLimit,
)
}
})
Expand Down Expand Up @@ -617,6 +622,181 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() {
s.Require().Equal(uint32(0), resp.Code)
}

func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
val := s.network.Validators[0]

granter := val.Address
info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
s.Require().NoError(err)
grantee := sdk.AccAddress(info.GetPubKey().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(10))).String()),
}
spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))

allowMsgs := "/cosmos.gov.v1beta1.Msg/SubmitProposal"

testCases := []struct {
name string
args []string
expectErr bool
respType proto.Message
expectedCode uint32
}{
{
"wrong granter",
append(
[]string{
"wrong granter",
"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,
},
{
"wrong grantee",
append(
[]string{
granter.String(),
"wrong grantee",
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.JSONMarshaler.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 := &types.FeeAllowanceGrant{}

s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), resp), out.String())
s.Require().Equal(resp.Grantee, resp.Grantee)
s.Require().Equal(resp.Granter, resp.Granter)

grant, err := resp.GetFeeGrant()
s.Require().NoError(err)

filteredFeeGrant, err := grant.(*types.AllowedMsgFeeAllowance).GetAllowance()
s.Require().NoError(err)

s.Require().Equal(
filteredFeeGrant.(*types.BasicFeeAllowance).SpendLimit.String(),
spendLimit.String(),
)

// exec filtered fee allowance
cases := []struct {
name string
malleate func() (testutil.BufferWriter, error)
expectErr bool
respType proto.Message
expectedCode uint32
}{
{
"valid tx",
func() (testutil.BufferWriter, error) {
return govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
"Text Proposal", "No desc", govtypes.ProposalTypeText,
fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
)
},
false,
&sdk.TxResponse{},
0,
},
{
"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.FlagFeeAccount, granter),
},
commonFlags...,
)
cmd := cli.NewCmdFeeGrant()
return clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
},
false, &sdk.TxResponse{}, 7,
},
}

for _, tc := range cases {
tc := tc

s.Run(tc.name, func() {
out, err := tc.malleate()

if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())

txResp := tc.respType.(*sdk.TxResponse)
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
}
})
}
}

func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}
Loading