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 13 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions x/feegrant/basic_fee.go
@@ -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
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
@@ -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
@@ -1,6 +1,8 @@
package feegrant

import (
"time"

"github.com/gogo/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec/types"
Expand Down Expand Up @@ -103,3 +105,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()
}
85 changes: 84 additions & 1 deletion x/feegrant/keeper/keeper.go
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,25 @@ func (k Keeper) GrantAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress,

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

var exp *time.Time
existingGrant, err := k.getGrant(ctx, grantee, granter)
if err != nil && existingGrant.GetAllowance() != nil {
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
grant1, err := existingGrant.GetGrant()
if err != nil {
return err
}

exp, err = grant1.ExpiresAt()
if err != nil {
return err
}

if exp != nil {
k.removeFromGrantQueue(ctx, feegrant.FeeAllowancePrefixQueue(exp, key))
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
}
}
atheeshp marked this conversation as resolved.
Show resolved Hide resolved

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

store.Set(key, bz)
expiration, err := feeAllowance.ExpiresAt()
atheeshp marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return err
} else if expiration != nil {
k.insertAllowanceKey(ctx, key, expiration)
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
Expand All @@ -72,6 +99,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 +237,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 +288,26 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (*feegrant.GenesisState, error) {
Allowances: grants,
}, err
}

func (k Keeper) removeFromGrantQueue(ctx sdk.Context, key []byte) {
store := ctx.KVStore(k.storeKey)
store.Delete(key)
}

func (k Keeper) insertAllowanceKey(ctx sdk.Context, grantKey []byte, exp *time.Time) {
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
store := ctx.KVStore(k.storeKey)
store.Set(feegrant.FeeAllowancePrefixQueue(exp, grantKey), grantKey)
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
}

// 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())
store.Delete(iterator.Value())
}
}
82 changes: 82 additions & 0 deletions x/feegrant/keeper/keeper_test.go
Expand Up @@ -257,5 +257,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
@@ -0,0 +1,21 @@
package keeper

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

// 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 v045.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc)
}
15 changes: 15 additions & 0 deletions x/feegrant/key.go
@@ -1,6 +1,8 @@
package feegrant

import (
time "time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
)
Expand All @@ -22,6 +24,9 @@ const (
var (
// FeeAllowanceKeyPrefix is the set of the kvstore for fee allowance data
FeeAllowanceKeyPrefix = []byte{0x00}

// FeeAllowanceQueueKeyPrefix is the set of the kvstore for fee allowance keys data
FeeAllowanceQueueKeyPrefix = []byte{0x01}
)

// FeeAllowanceKey is the canonical key to store a grant from granter to grantee
Expand All @@ -34,3 +39,13 @@ func FeeAllowanceKey(granter sdk.AccAddress, grantee sdk.AccAddress) []byte {
func FeeAllowancePrefixByGrantee(grantee sdk.AccAddress) []byte {
return append(FeeAllowanceKeyPrefix, address.MustLengthPrefix(grantee.Bytes())...)
}

// FeeAllowancePrefixQueue is the canonical key to store grant key.
func FeeAllowancePrefixQueue(exp *time.Time, allowanceKey []byte) []byte {
allowanceByExpTimeKey := AllowanceByExpTimeKey(exp)
return append(allowanceByExpTimeKey, allowanceKey[1:]...)
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
}

func AllowanceByExpTimeKey(exp *time.Time) []byte {
return append(FeeAllowanceQueueKeyPrefix, sdk.FormatTimeBytes(*exp)...)
}
5 changes: 5 additions & 0 deletions x/feegrant/migrations/v045/keys.go
@@ -0,0 +1,5 @@
package v045
atheeshp marked this conversation as resolved.
Show resolved Hide resolved

var (
ModuleName = "feegrant"
)
50 changes: 50 additions & 0 deletions x/feegrant/migrations/v045/store.go
@@ -0,0 +1,50 @@
package v045

import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/feegrant"
)

func addAllowancesByExpTimeQueue(ctx types.Context, store storetypes.KVStore, cdc codec.BinaryCodec) error {
prefixStore := prefix.NewStore(store, feegrant.FeeAllowanceKeyPrefix)
iterator := prefixStore.Iterator(nil, nil)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
var grant feegrant.Grant
bz := iterator.Value()
if err := cdc.Unmarshal(bz, &grant); err != nil {
return err
}

grantInfo, err := grant.GetGrant()
if err != nil {
return err
}

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

if exp != nil {
key := iterator.Key()
if exp.Before(ctx.BlockTime()) {
prefixStore.Delete(key)
} else {
grantByExpTimeQueueKey := feegrant.FeeAllowancePrefixQueue(exp, key[1:])
atheeshp marked this conversation as resolved.
Show resolved Hide resolved
store.Set(grantByExpTimeQueueKey, key)
}
}
}

return nil
}

func MigrateStore(ctx types.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)
return addAllowancesByExpTimeQueue(ctx, store, cdc)
}