Skip to content

Commit

Permalink
Merge branch 'release/v0.50.x' into mergify/bp/release/v0.50.x/pr-18166
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrbrt committed Oct 23, 2023
2 parents e18a6e8 + 026631c commit 9ba23cd
Show file tree
Hide file tree
Showing 16 changed files with 1,465 additions and 72 deletions.
962 changes: 924 additions & 38 deletions api/cosmos/feegrant/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions api/cosmos/feegrant/v1beta1/tx_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions proto/cosmos/feegrant/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ service Msg {
// RevokeAllowance revokes any fee allowance of granter's account that
// has been granted to the grantee.
rpc RevokeAllowance(MsgRevokeAllowance) returns (MsgRevokeAllowanceResponse);

// PruneAllowances prunes expired fee allowances, currently up to 75 at a time.
//
// Since cosmos-sdk 0.50
rpc PruneAllowances(MsgPruneAllowances) returns (MsgPruneAllowancesResponse);
}

// MsgGrantAllowance adds permission for Grantee to spend up to Allowance
Expand Down Expand Up @@ -55,3 +60,18 @@ message MsgRevokeAllowance {

// MsgRevokeAllowanceResponse defines the Msg/RevokeAllowanceResponse response type.
message MsgRevokeAllowanceResponse {}

// MsgPruneAllowances prunes expired fee allowances.
//
// Since cosmos-sdk 0.50
message MsgPruneAllowances {
option (cosmos.msg.v1.signer) = "pruner";

// pruner is the address of the user pruning expired allowances.
string pruner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

// MsgPruneAllowancesResponse defines the Msg/PruneAllowancesResponse response type.
//
// Since cosmos-sdk 0.50
message MsgPruneAllowancesResponse {}
6 changes: 5 additions & 1 deletion x/authz/generic_authorization.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package authz

import (
context "context"
"context"
"errors"

sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand All @@ -27,5 +28,8 @@ func (a GenericAuthorization) Accept(ctx context.Context, msg sdk.Msg) (AcceptRe

// ValidateBasic implements Authorization.ValidateBasic.
func (a GenericAuthorization) ValidateBasic() error {
if a.Msg == "" {
return errors.New("msg type cannot be empty")
}
return nil
}
1 change: 1 addition & 0 deletions x/feegrant/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* [#18047](https://github.com/cosmos/cosmos-sdk/pull/18047) Added a limit of 200 grants pruned per EndBlock and the method PruneAllowances that prunes 75 expired grants on every run.
* [#14649](https://github.com/cosmos/cosmos-sdk/pull/14649) The `x/feegrant` module is extracted to have a separate go.mod file which allows it to be a standalone module.

### API Breaking Changes
Expand Down
8 changes: 8 additions & 0 deletions x/feegrant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ The feegrant module emits the following events:
| message | granter | {granterAddress} |
| message | grantee | {granteeAddress} |

### Prune fee allowances

| Type | Attribute Key | Attribute Value |
| ------- | ------------- | ---------------- |
| message | action | prune_feegrant |
| message | pruner | {prunerAddress} |


## Client

### CLI
Expand Down
2 changes: 2 additions & 0 deletions x/feegrant/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ const (
EventTypeRevokeFeeGrant = "revoke_feegrant"
EventTypeSetFeeGrant = "set_feegrant"
EventTypeUpdateFeeGrant = "update_feegrant"
EventTypePruneFeeGrant = "prune_feegrant"

AttributeKeyGranter = "granter"
AttributeKeyGrantee = "grantee"
AttributeKeyPruner = "pruner"
)
16 changes: 9 additions & 7 deletions x/feegrant/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,7 @@ func (k Keeper) IterateAllFeeAllowances(ctx context.Context, cb func(grant feegr

// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee
func (k Keeper) UseGrantedFees(ctx context.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error {
f, err := k.getGrant(ctx, granter, grantee)
if err != nil {
return err
}

grant, err := f.GetGrant()
grant, err := k.GetAllowance(ctx, granter, grantee)
if err != nil {
return err
}
Expand Down Expand Up @@ -322,10 +317,11 @@ func (k Keeper) addToFeeAllowanceQueue(ctx context.Context, grantKey []byte, exp
}

// RemoveExpiredAllowances iterates grantsByExpiryQueue and deletes the expired grants.
func (k Keeper) RemoveExpiredAllowances(ctx context.Context) error {
func (k Keeper) RemoveExpiredAllowances(ctx context.Context, limit int32) error {
exp := sdk.UnwrapSDKContext(ctx).BlockTime()
store := k.storeService.OpenKVStore(ctx)
iterator, err := store.Iterator(feegrant.FeeAllowanceQueueKeyPrefix, storetypes.InclusiveEndBytes(feegrant.AllowanceByExpTimeKey(&exp)))
var count int32
if err != nil {
return err
}
Expand All @@ -342,6 +338,12 @@ func (k Keeper) RemoveExpiredAllowances(ctx context.Context) error {
if err != nil {
return err
}

// limit the amount of iterations to avoid taking too much time
count++
if count == limit {
return nil
}
}
return nil
}
13 changes: 7 additions & 6 deletions x/feegrant/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,17 @@ func TestKeeperTestSuite(t *testing.T) {
}

func (suite *KeeperTestSuite) SetupTest() {
suite.addrs = simtestutil.CreateIncrementalAccounts(4)
suite.addrs = simtestutil.CreateIncrementalAccounts(20)
key := storetypes.NewKVStoreKey(feegrant.StoreKey)
testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test"))
encCfg := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{})

// setup gomock and initialize some globally expected executions
ctrl := gomock.NewController(suite.T())
suite.accountKeeper = feegranttestutil.NewMockAccountKeeper(ctrl)
suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[0]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[0])).AnyTimes()
suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[1]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[1])).AnyTimes()
suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[2]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[2])).AnyTimes()
suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[3]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[3])).AnyTimes()
for i := 0; i < len(suite.addrs); i++ {
suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[i]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[i])).AnyTimes()
}

suite.accountKeeper.EXPECT().AddressCodec().Return(codecaddress.NewBech32Codec("cosmos")).AnyTimes()

Expand Down Expand Up @@ -395,7 +394,9 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
}
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, tc.granter, tc.grantee, tc.allowance)
suite.NoError(err)
suite.feegrantKeeper.RemoveExpiredAllowances(tc.ctx)
err = suite.feegrantKeeper.RemoveExpiredAllowances(tc.ctx, 5)
suite.NoError(err)

grant, err := suite.feegrantKeeper.GetAllowance(tc.ctx, tc.granter, tc.grantee)
if tc.expErrMsg != "" {
suite.Error(err)
Expand Down
19 changes: 19 additions & 0 deletions x/feegrant/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,22 @@ func (k msgServer) RevokeAllowance(goCtx context.Context, msg *feegrant.MsgRevok

return &feegrant.MsgRevokeAllowanceResponse{}, nil
}

// PruneAllowances removes expired allowances from the store.
func (k msgServer) PruneAllowances(ctx context.Context, req *feegrant.MsgPruneAllowances) (*feegrant.MsgPruneAllowancesResponse, error) {
// 75 is an arbitrary value, we can change it later if needed
err := k.RemoveExpiredAllowances(ctx, 75)
if err != nil {
return nil, err
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
feegrant.EventTypePruneFeeGrant,
sdk.NewAttribute(feegrant.AttributeKeyPruner, req.Pruner),
),
)

return &feegrant.MsgPruneAllowancesResponse{}, nil
}
63 changes: 63 additions & 0 deletions x/feegrant/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,66 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() {
})
}
}

func (suite *KeeperTestSuite) TestPruneAllowances() {
ctx := suite.ctx.WithBlockTime(time.Now())
oneYear := ctx.BlockTime().AddDate(1, 0, 0)

// We create 76 allowances, all expiring in one year
count := 0
for i := 0; i < len(suite.addrs); i++ {
for j := 0; j < len(suite.addrs); j++ {
if count == 76 {
break
}
if suite.addrs[i].String() == suite.addrs[j].String() {
continue
}

any, err := codectypes.NewAnyWithValue(&feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &oneYear,
})
suite.Require().NoError(err)
req := &feegrant.MsgGrantAllowance{
Granter: suite.addrs[i].String(),
Grantee: suite.addrs[j].String(),
Allowance: any,
}

_, err = suite.msgSrvr.GrantAllowance(ctx, req)
if err != nil {
// do not fail, just try with another pair
continue
}

count++
}
}

// we have 76 allowances
count = 0
err := suite.feegrantKeeper.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool {
count++
return false
})
suite.Require().NoError(err)
suite.Require().Equal(76, count)

// after a year and one day passes, they are all expired
oneYearAndADay := ctx.BlockTime().AddDate(1, 0, 1)
ctx = suite.ctx.WithBlockTime(oneYearAndADay)

// we prune them, but currently only 75 will be pruned
_, err = suite.msgSrvr.PruneAllowances(ctx, &feegrant.MsgPruneAllowances{})
suite.Require().NoError(err)

// we have 1 allowance left
count = 0
err = suite.feegrantKeeper.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool {
count++
return false
})
suite.Require().NoError(err)
suite.Require().Equal(1, count)
}
12 changes: 5 additions & 7 deletions x/feegrant/module/abci.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package module

import (
"cosmossdk.io/x/feegrant/keeper"
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"cosmossdk.io/x/feegrant/keeper"
)

func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
err := k.RemoveExpiredAllowances(ctx)
if err != nil {
panic(err)
}
func EndBlocker(ctx context.Context, k keeper.Keeper) error {
// 200 is an arbitrary value, we can change it later if needed
return k.RemoveExpiredAllowances(ctx, 200)
}
2 changes: 1 addition & 1 deletion x/feegrant/module/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestFeegrantPruning(t *testing.T) {
feegrant.RegisterQueryServer(queryHelper, feegrantKeeper)
queryClient := feegrant.NewQueryClient(queryHelper)

module.EndBlocker(testCtx.Ctx, feegrantKeeper)
require.NoError(t, module.EndBlocker(testCtx.Ctx, feegrantKeeper))

res, err := queryClient.Allowances(testCtx.Ctx.Context(), &feegrant.QueryAllowancesRequest{
Grantee: grantee.String(),
Expand Down
7 changes: 7 additions & 0 deletions x/feegrant/module/autocli.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
{ProtoField: "grantee"},
},
},
{
RpcMethod: "PruneAllowances",
Use: "prune",
Short: "Prune expired allowances",
Long: "Prune up to 75 expired allowances in order to reduce the size of the store when the number of expired allowances is large.",
Example: fmt.Sprintf(`$ %s tx feegrant prune --from [mykey]`, version.AppName),
},
},
EnhanceCustomCommand: true,
},
Expand Down
4 changes: 1 addition & 3 deletions x/feegrant/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ func (AppModule) ConsensusVersion() uint64 { return 2 }
// EndBlock returns the end blocker for the feegrant module. It returns no validator
// updates.
func (am AppModule) EndBlock(ctx context.Context) error {
c := sdk.UnwrapSDKContext(ctx)
EndBlocker(c, am.keeper)
return nil
return EndBlocker(ctx, am.keeper)
}

func init() {
Expand Down
Loading

0 comments on commit 9ba23cd

Please sign in to comment.