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

feat: adds pruning for feegrant #10830

Merged
merged 55 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
152e37d
WIP feegrant pruning
atheeshp Dec 14, 2021
b631f0c
add queue for feegrant keys with expiry time
atheeshp Dec 16, 2021
9cc543f
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Dec 16, 2021
88eb828
add endblocker
atheeshp Dec 16, 2021
903c487
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Dec 21, 2021
39ef3b9
add tests
atheeshp Dec 21, 2021
4c72059
changes
atheeshp Dec 21, 2021
59faebf
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Dec 22, 2021
a67604a
added tests for migration
atheeshp Dec 22, 2021
e2304da
add abci tests
atheeshp Dec 22, 2021
5c07e7a
review changes
atheeshp Dec 23, 2021
0d6c50a
review changes
atheeshp Dec 23, 2021
f5d584d
Merge branch 'master' into ap/feegrant-pruning
atheeshp Dec 23, 2021
9e287d8
remove value storage
atheeshp Jan 5, 2022
f1b228e
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 5, 2022
b10024c
Merge branch 'master' into ap/feegrant-pruning
atheeshp Jan 6, 2022
fbcf8ef
Merge branch 'master' into ap/feegrant-pruning
atheeshp Jan 10, 2022
24fa5cd
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 19, 2022
36dbffc
review changes
atheeshp Jan 19, 2022
d32040a
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 21, 2022
9fb03f8
address review changes
atheeshp Jan 21, 2022
46450c4
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 24, 2022
5b3dd79
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 25, 2022
248aced
fix tests
atheeshp Jan 25, 2022
07dc9fd
Merge branch 'master' into ap/feegrant-pruning
atheeshp Jan 25, 2022
eb9e5ec
add log
atheeshp Jan 25, 2022
5b7011e
Merge branch 'ap/feegrant-pruning' of github.com:cosmos/cosmos-sdk in…
atheeshp Jan 25, 2022
45e819e
add log
atheeshp Jan 25, 2022
5bf4738
revert
atheeshp Jan 25, 2022
da08402
add error
atheeshp Jan 25, 2022
52cb90f
revert
atheeshp Jan 25, 2022
6bf3a91
Merge branch 'master' into ap/feegrant-pruning
atheeshp Jan 25, 2022
bf0e3d8
Merge branch 'ap/feegrant-pruning' of https://github.com/cosmos/cosmo…
aleem1314 Jan 27, 2022
3cda099
chore: add logs
aleem1314 Jan 27, 2022
38192e4
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 27, 2022
0c01fcd
Merge branch 'ap/feegrant-pruning' of github.com:cosmos/cosmos-sdk in…
atheeshp Jan 27, 2022
324dfa5
fix tests
atheeshp Jan 27, 2022
7091b40
revert
atheeshp Jan 27, 2022
9b8e089
Merge branch 'master' into ap/feegrant-pruning
atheeshp Jan 27, 2022
3c0a17f
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Jan 31, 2022
d8d626a
review changes
atheeshp Jan 31, 2022
bb3999a
Merge branch 'ap/feegrant-pruning' of github.com:cosmos/cosmos-sdk in…
atheeshp Jan 31, 2022
32f7de9
Merge branch 'master' into ap/feegrant-pruning
atheeshp Feb 3, 2022
cac6a15
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Feb 4, 2022
214a597
Merge branch 'ap/feegrant-pruning' of github.com:cosmos/cosmos-sdk in…
atheeshp Feb 4, 2022
d587629
Update x/feegrant/spec/01_concepts.md
atheeshp Feb 4, 2022
1133b59
review changes
atheeshp Feb 4, 2022
4c5bdc0
Merge branch 'ap/feegrant-pruning' of github.com:cosmos/cosmos-sdk in…
atheeshp Feb 4, 2022
e9e1be7
update comment
atheeshp Feb 4, 2022
da90283
Merge branch 'master' of github.com:cosmos/cosmos-sdk into ap/feegran…
atheeshp Feb 7, 2022
4504b50
update change log
atheeshp Feb 7, 2022
7cc889d
Merge branch 'master' into ap/feegrant-pruning
mergify[bot] Feb 7, 2022
4c74c60
Merge branch 'master' into ap/feegrant-pruning
blushi Feb 7, 2022
d13eac1
Merge branch 'master' into ap/feegrant-pruning
mergify[bot] Feb 7, 2022
5322d9a
Merge branch 'master' into ap/feegrant-pruning
atheeshp Feb 8, 2022
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
6 changes: 6 additions & 0 deletions x/feegrant/basic_fee.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package feegrant

import (
time "time"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
Expand Down Expand Up @@ -52,3 +54,7 @@ func (a BasicAllowance) ValidateBasic() error {

return nil
}

func (a BasicAllowance) ExpiresAt() (*time.Time, error) {
return a.Expiration, nil
}
robert-zaremba marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions x/feegrant/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
EventTypeUseFeeGrant = "use_feegrant"
EventTypeRevokeFeeGrant = "revoke_feegrant"
EventTypeSetFeeGrant = "set_feegrant"
EventTypeUpdateFeeGrant = "update_feegrant"

AttributeKeyGranter = "granter"
AttributeKeyGrantee = "grantee"
Expand Down
5 changes: 5 additions & 0 deletions x/feegrant/fees.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package feegrant

import (
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand All @@ -22,4 +24,7 @@ type FeeAllowanceI interface {
// ValidateBasic should evaluate this FeeAllowance for internal consistency.
// Don't allow negative amounts, or negative periods for example.
ValidateBasic() error

// ExpiresAt returns the expiry time of the allowance.
ExpiresAt() (*time.Time, error)
}
10 changes: 10 additions & 0 deletions x/feegrant/filtered_fee.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package feegrant

import (
"time"

"github.com/gogo/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec/types"
Expand Down Expand Up @@ -120,3 +122,11 @@ func (a *AllowedMsgAllowance) ValidateBasic() error {

return allowance.ValidateBasic()
}

func (a *AllowedMsgAllowance) ExpiresAt() (*time.Time, error) {
allowance, err := a.GetAllowance()
if err != nil {
return nil, err
}
return allowance.ExpiresAt()
}
3 changes: 2 additions & 1 deletion x/feegrant/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ func (q Keeper) AllowancesByGranter(c context.Context, req *feegrant.QueryAllowa
var grants []*feegrant.Grant

store := ctx.KVStore(q.storeKey)
pageRes, err := query.Paginate(store, req.Pagination, func(key []byte, value []byte) error {
prefixStore := prefix.NewStore(store, feegrant.FeeAllowanceKeyPrefix)
pageRes, err := query.Paginate(prefixStore, req.Pagination, func(key []byte, value []byte) error {
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
var grant feegrant.Grant

granter, _ := feegrant.ParseAddressesFromFeeAllowanceKey(key)
Expand Down
102 changes: 101 additions & 1 deletion x/feegrant/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"fmt"
"time"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/tendermint/tendermint/libs/log"
Expand Down Expand Up @@ -49,6 +50,45 @@ func (k Keeper) GrantAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress,

store := ctx.KVStore(k.storeKey)
key := feegrant.FeeAllowanceKey(granter, grantee)

var oldExp *time.Time
existingGrant, err := k.getGrant(ctx, grantee, granter)
if err != nil && existingGrant != nil && existingGrant.GetAllowance() != nil {
grantInfo, err := existingGrant.GetGrant()
if err != nil {
return err
}

oldExp, err = grantInfo.ExpiresAt()
if err != nil {
return err
}
}

newExp, err := feeAllowance.ExpiresAt()
if err != nil {
return err
} else if newExp != nil && newExp.Before(ctx.BlockTime()) {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "expiration is before current block time")
} else if oldExp == nil && newExp != nil {
// when old oldExp is nil there won't be any key added before to queue.
// add the new key to queue directly.
k.addToFeeAllowanceQueue(ctx, key[1:], newExp)
} else if oldExp != nil && newExp == nil {
// when newExp is nil no need of adding the key to the pruning queue
// remove the old key from queue.
k.removeFromGrantQueue(ctx, oldExp, key[1:])
} else if oldExp != nil && newExp != nil && !oldExp.Equal(*newExp) {
// `key` formed here with the prefix of `FeeAllowanceKeyPrefix` (which is `0x00`)
// remove the 1st byte and reuse the remaining key as it is.

// remove the old key from queue.
k.removeFromGrantQueue(ctx, oldExp, key[1:])

// add the new key to queue.
k.addToFeeAllowanceQueue(ctx, key[1:], newExp)
}
atheeshp marked this conversation as resolved.
Show resolved Hide resolved

grant, err := feegrant.NewGrant(granter, grantee, feeAllowance)
if err != nil {
return err
Expand All @@ -72,6 +112,39 @@ func (k Keeper) GrantAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress,
return nil
}

// UpdateAllowance updates the existing grant.
func (k Keeper) UpdateAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance feegrant.FeeAllowanceI) error {
store := ctx.KVStore(k.storeKey)
key := feegrant.FeeAllowanceKey(granter, grantee)

_, err := k.getGrant(ctx, granter, grantee)
if err != nil {
return err
}

grant, err := feegrant.NewGrant(granter, grantee, feeAllowance)
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

bz, err := k.cdc.Marshal(&grant)
if err != nil {
return err
}

store.Set(key, bz)

ctx.EventManager().EmitEvent(
sdk.NewEvent(
feegrant.EventTypeUpdateFeeGrant,
sdk.NewAttribute(feegrant.AttributeKeyGranter, grant.Granter),
sdk.NewAttribute(feegrant.AttributeKeyGrantee, grant.Grantee),
),
)

return nil
}

// revokeAllowance removes an existing grant
func (k Keeper) revokeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) error {
_, err := k.getGrant(ctx, granter, grantee)
Expand Down Expand Up @@ -177,7 +250,7 @@ func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress,
emitUseGrantEvent(ctx, granter.String(), grantee.String())

// if fee allowance is accepted, store the updated state of the allowance
return k.GrantAllowance(ctx, granter, grantee, grant)
return k.UpdateAllowance(ctx, granter, grantee, grant)
}

func emitUseGrantEvent(ctx sdk.Context, granter, grantee string) {
Expand Down Expand Up @@ -228,3 +301,30 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (*feegrant.GenesisState, error) {
Allowances: grants,
}, err
}

func (k Keeper) removeFromGrantQueue(ctx sdk.Context, exp *time.Time, allowanceKey []byte) {
key := feegrant.FeeAllowancePrefixQueue(exp, allowanceKey)
store := ctx.KVStore(k.storeKey)
store.Delete(key)
}

func (k Keeper) addToFeeAllowanceQueue(ctx sdk.Context, grantKey []byte, exp *time.Time) {
store := ctx.KVStore(k.storeKey)
store.Set(feegrant.FeeAllowancePrefixQueue(exp, grantKey), []byte{})
}

// RemoveExpiredAllowances iterates grantsByExpiryQueue and deletes the expired grants.
func (k Keeper) RemoveExpiredAllowances(ctx sdk.Context) {
exp := ctx.BlockTime()
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(feegrant.FeeAllowanceQueueKeyPrefix, sdk.InclusiveEndBytes(feegrant.AllowanceByExpTimeKey(&exp)))
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
expLen := len(sdk.FormatTimeBytes(ctx.BlockTime()))

// extract the fee allowance key by removing the allowance queue prefix length, expiration length from key.
store.Delete(append(feegrant.FeeAllowanceKeyPrefix, iterator.Key()[1+expLen:]...))
}
}
94 changes: 89 additions & 5 deletions x/feegrant/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,18 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
})
}

expired := &feegrant.BasicAllowance{
basicAllowance := &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &blockTime,
}
// creating expired feegrant
ctx := suite.sdkCtx.WithBlockTime(oneYear)
err := suite.keeper.GrantAllowance(ctx, suite.addrs[0], suite.addrs[2], expired)

// create basic fee allowance
err := suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[2], basicAllowance)
suite.Require().NoError(err)

// waiting for future blocks, allowance to be pruned.
ctx := suite.sdkCtx.WithBlockTime(oneYear)

// expect error: feegrant expired
err = suite.keeper.UseGrantedFees(ctx, suite.addrs[0], suite.addrs[2], eth, []sdk.Msg{})
suite.Error(err)
Expand All @@ -232,7 +235,6 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
_, err = suite.keeper.GetAllowance(ctx, suite.addrs[0], suite.addrs[2])
suite.Error(err)
suite.Contains(err.Error(), "fee-grant not found")

}

func (suite *KeeperTestSuite) TestIterateGrants() {
Expand All @@ -257,5 +259,87 @@ func (suite *KeeperTestSuite) TestIterateGrants() {
suite.Require().Contains([]string{suite.addrs[0].String(), suite.addrs[2].String()}, grant.Granter)
return true
})
}

func (suite *KeeperTestSuite) TestPruneGrants() {
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
now := suite.sdkCtx.BlockTime()
oneYearExpiry := now.AddDate(1, 0, 0)

testCases := []struct {
name string
ctx sdk.Context
granter sdk.AccAddress
grantee sdk.AccAddress
allowance feegrant.FeeAllowanceI
expErrMsg string
}{
{
name: "grant not pruned from state",
ctx: suite.sdkCtx,
granter: suite.addrs[0],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &now,
},
},
{
name: "grant pruned from state after a block: error",
ctx: suite.sdkCtx.WithBlockTime(now.AddDate(0, 0, 1)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
expErrMsg: "not found",
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &now,
},
},
{
name: "grant not pruned from state after a day: no error",
ctx: suite.sdkCtx.WithBlockTime(now.AddDate(0, 0, 1)),
granter: suite.addrs[1],
grantee: suite.addrs[0],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneYearExpiry,
},
},
{
name: "grant pruned from state after a year: error",
ctx: suite.sdkCtx.WithBlockTime(now.AddDate(1, 0, 1)),
granter: suite.addrs[1],
grantee: suite.addrs[2],
expErrMsg: "not found",
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneYearExpiry,
},
},
{
name: "no expiry: no error",
ctx: suite.sdkCtx.WithBlockTime(now.AddDate(1, 0, 0)),
granter: suite.addrs[1],
grantee: suite.addrs[2],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneYearExpiry,
},
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.keeper.GrantAllowance(suite.sdkCtx, tc.granter, tc.grantee, tc.allowance)
suite.app.FeeGrantKeeper.RemoveExpiredAllowances(tc.ctx)
grant, err := suite.keeper.GetAllowance(tc.ctx, tc.granter, tc.grantee)
if tc.expErrMsg != "" {
suite.Error(err)
suite.Contains(err.Error(), tc.expErrMsg)
} else {
suite.NotNil(grant)
}
})
}
}
21 changes: 21 additions & 0 deletions x/feegrant/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
v046 "github.com/cosmos/cosmos-sdk/x/feegrant/migrations/v046"
)

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper Keeper
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper Keeper) Migrator {
return Migrator{keeper: keeper}
}

// Migrate1to2 migrates from version 1 to 2.
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v046.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
}
Loading