refactor(feegrant): collection migration (#16535)

This commit is contained in:
atheeshp 2023-07-28 20:46:00 +05:30 committed by GitHub
parent 009a152274
commit e51e74a9df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 120 additions and 247 deletions

View File

@ -56,6 +56,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* (x/feegrant) [16535](https://github.com/cosmos/cosmos-sdk/pull/16535) Use collections for `FeeAllowance`, `FeeAllowanceQueue`.
* (x/staking) [17063](https://github.com/cosmos/cosmos-sdk/pull/17063) Use collections for `HistoricalInfo`:
* remove `Keeper`: `GetHistoricalInfo`, `SetHistoricalInfo`,
* (x/staking) [17062](https://github.com/cosmos/cosmos-sdk/pull/17062) Use collections for `ValidatorUpdates`:

View File

@ -5,7 +5,7 @@ go 1.20
require (
cosmossdk.io/api v0.7.0
cosmossdk.io/client/v2 v2.0.0-20230630094428-02b760776860
cosmossdk.io/collections v0.3.0
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7
cosmossdk.io/core v0.9.0
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca

View File

@ -189,8 +189,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4=
cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M=
cosmossdk.io/collections v0.3.0 h1:v0eEqLBxebAV+t+Ahwf9tSJOu95HVLINwROXx2TTZ08=
cosmossdk.io/collections v0.3.0/go.mod h1:CHE1+niUElL9ikCpevRZcp0yqQ4TU0TrEEGirN0mvIg=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7 h1:4XhcAIVBXPwCFTT9abOzZZaEadbRaVws8A6UTr2KGIE=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7/go.mod h1:+KJND4gZHilxE3meopEl/Uet6IAw3PyiSbgeOrFzAZE=
cosmossdk.io/core v0.9.0 h1:30ScAOHDIUOCg1DKAwqkho9wuQJnu7GUrMcg0XLioic=
cosmossdk.io/core v0.9.0/go.mod h1:NFgl5r41Q36+RixTvyrfsS6qQ65agCbZ1FTpnN7/G1Y=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=

View File

@ -4,7 +4,7 @@ go 1.20
require (
cosmossdk.io/api v0.7.0
cosmossdk.io/collections v0.3.0
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7
cosmossdk.io/core v0.9.0
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0

View File

@ -189,8 +189,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4=
cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M=
cosmossdk.io/collections v0.3.0 h1:v0eEqLBxebAV+t+Ahwf9tSJOu95HVLINwROXx2TTZ08=
cosmossdk.io/collections v0.3.0/go.mod h1:CHE1+niUElL9ikCpevRZcp0yqQ4TU0TrEEGirN0mvIg=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7 h1:4XhcAIVBXPwCFTT9abOzZZaEadbRaVws8A6UTr2KGIE=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7/go.mod h1:+KJND4gZHilxE3meopEl/Uet6IAw3PyiSbgeOrFzAZE=
cosmossdk.io/core v0.9.0 h1:30ScAOHDIUOCg1DKAwqkho9wuQJnu7GUrMcg0XLioic=
cosmossdk.io/core v0.9.0/go.mod h1:NFgl5r41Q36+RixTvyrfsS6qQ65agCbZ1FTpnN7/G1Y=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=

View File

@ -4,6 +4,7 @@ go 1.20
require (
cosmossdk.io/api v0.7.0
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7
cosmossdk.io/core v0.9.0
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0
@ -12,7 +13,7 @@ require (
cosmossdk.io/store v1.0.0-alpha.1
github.com/cometbft/cometbft v0.38.0-rc3
github.com/cosmos/cosmos-proto v1.0.0-beta.3
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230713093628-90d9a75d4125
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230727092431-f0f777fa3cb7
github.com/cosmos/gogoproto v1.4.10
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.3
@ -26,7 +27,6 @@ require (
)
require (
cosmossdk.io/collections v0.3.0 // indirect
cosmossdk.io/x/tx v0.9.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect

View File

@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4=
cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M=
cosmossdk.io/collections v0.3.0 h1:v0eEqLBxebAV+t+Ahwf9tSJOu95HVLINwROXx2TTZ08=
cosmossdk.io/collections v0.3.0/go.mod h1:CHE1+niUElL9ikCpevRZcp0yqQ4TU0TrEEGirN0mvIg=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7 h1:4XhcAIVBXPwCFTT9abOzZZaEadbRaVws8A6UTr2KGIE=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7/go.mod h1:+KJND4gZHilxE3meopEl/Uet6IAw3PyiSbgeOrFzAZE=
cosmossdk.io/core v0.9.0 h1:30ScAOHDIUOCg1DKAwqkho9wuQJnu7GUrMcg0XLioic=
cosmossdk.io/core v0.9.0/go.mod h1:NFgl5r41Q36+RixTvyrfsS6qQ65agCbZ1FTpnN7/G1Y=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
@ -173,8 +173,8 @@ github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0
github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U=
github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o=
github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230713093628-90d9a75d4125 h1:2aGCqfxWf2AAvLOUHaRiByle6n0FPRdeOF/62JTldh0=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230713093628-90d9a75d4125/go.mod h1:LME6v5XztqVK7/1uTQj/G6ZJdosJEz24rKaPYk+WbqI=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230727092431-f0f777fa3cb7 h1:ZbRIti9b4rTLuLB9ZM5XAMev1AYi9eOoo0ide5vIstM=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230727092431-f0f777fa3cb7/go.mod h1:fKr/+xe4ZSzj38HwFBvxz/+LZ6v4e9bknD7vq6AJXzY=
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE=

View File

@ -1,18 +1,16 @@
package keeper
import (
"bytes"
"context"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"cosmossdk.io/store/prefix"
"cosmossdk.io/collections"
"cosmossdk.io/x/feegrant"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
)
@ -72,23 +70,16 @@ func (q Keeper) Allowances(c context.Context, req *feegrant.QueryAllowancesReque
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
var grants []*feegrant.Grant
store := q.storeService.OpenKVStore(ctx)
grantsStore := prefix.NewStore(runtime.KVStoreAdapter(store), feegrant.FeeAllowancePrefixByGrantee(granteeAddr))
pageRes, err := query.Paginate(grantsStore, req.Pagination, func(key, value []byte) error {
var grant feegrant.Grant
if err := q.cdc.Unmarshal(value, &grant); err != nil {
return err
}
grants = append(grants, &grant)
return nil
})
_, pageRes, err := query.CollectionFilteredPaginate(c, q.FeeAllowance, req.Pagination,
func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (include bool, err error) {
grants = append(grants, &grant)
return true, nil
}, func(_ collections.Pair[sdk.AccAddress, sdk.AccAddress], value feegrant.Grant) (*feegrant.Grant, error) {
return &value, nil
}, query.WithCollectionPaginationPairPrefix[sdk.AccAddress, sdk.AccAddress](granteeAddr),
)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
@ -107,21 +98,20 @@ func (q Keeper) AllowancesByGranter(c context.Context, req *feegrant.QueryAllowa
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
var grants []*feegrant.Grant
_, pageRes, err := query.CollectionFilteredPaginate(c, q.FeeAllowance, req.Pagination,
func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (include bool, err error) {
if !sdk.AccAddress(granterAddr).Equals(key.K2()) {
return false, nil
}
store := q.storeService.OpenKVStore(ctx)
prefixStore := prefix.NewStore(runtime.KVStoreAdapter(store), feegrant.FeeAllowanceKeyPrefix)
grants, pageRes, err := query.GenericFilteredPaginate(q.cdc, prefixStore, req.Pagination, func(key []byte, grant *feegrant.Grant) (*feegrant.Grant, error) {
// ParseAddressesFromFeeAllowanceKey expects the full key including the prefix.
granter, _ := feegrant.ParseAddressesFromFeeAllowanceKey(append(feegrant.FeeAllowanceKeyPrefix, key...))
if !bytes.Equal(granter, granterAddr) {
return nil, nil
}
return grant, nil
}, func() *feegrant.Grant {
return &feegrant.Grant{}
})
grants = append(grants, &grant)
return true, nil
},
func(_ collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (*feegrant.Grant, error) {
return &grant, nil
},
)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

View File

@ -2,9 +2,11 @@ package keeper
import (
"context"
"errors"
"fmt"
"time"
"cosmossdk.io/collections"
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
@ -21,19 +23,38 @@ import (
// Keeper manages state of all fee grants, as well as calculating approval.
// It must have a codec with all available allowances registered.
type Keeper struct {
cdc codec.BinaryCodec
storeService store.KVStoreService
authKeeper feegrant.AccountKeeper
cdc codec.BinaryCodec
storeService store.KVStoreService
authKeeper feegrant.AccountKeeper
Schema collections.Schema
FeeAllowance collections.Map[collections.Pair[sdk.AccAddress, sdk.AccAddress], feegrant.Grant]
FeeAllowanceQueue collections.Map[collections.Triple[time.Time, sdk.AccAddress, sdk.AccAddress], bool]
}
var _ ante.FeegrantKeeper = &Keeper{}
// NewKeeper creates a feegrant Keeper
func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak feegrant.AccountKeeper) Keeper {
sb := collections.NewSchemaBuilder(storeService)
return Keeper{
cdc: cdc,
storeService: storeService,
authKeeper: ak,
FeeAllowance: collections.NewMap(
sb,
feegrant.FeeAllowanceKeyPrefix,
"allowances",
collections.PairKeyCodec(sdk.LengthPrefixedAddressKey(sdk.AccAddressKey), sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
codec.CollValue[feegrant.Grant](cdc),
),
FeeAllowanceQueue: collections.NewMap(
sb,
feegrant.FeeAllowanceQueueKeyPrefix,
"allowances_queue",
collections.TripleKeyCodec(sdk.TimeKey, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey), sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
collections.BoolValue,
),
}
}
@ -56,9 +77,6 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr
k.authKeeper.SetAccount(ctx, granteeAcc)
}
store := k.storeService.OpenKVStore(ctx)
key := feegrant.FeeAllowanceKey(granter, grantee)
exp, err := feeAllowance.ExpiresAt()
if err != nil {
return err
@ -72,9 +90,7 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr
// if expiry is not nil, add the new key to pruning queue.
if exp != nil {
// `key` formed here with the prefix of `FeeAllowanceKeyPrefix` (which is `0x00`)
// remove the 1st byte and reuse the remaining key as it is
err = k.addToFeeAllowanceQueue(ctx, key[1:], exp)
err = k.FeeAllowanceQueue.Set(ctx, collections.Join3(*exp, grantee, granter), true)
if err != nil {
return err
}
@ -85,13 +101,7 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr
return err
}
bz, err := k.cdc.Marshal(&grant)
if err != nil {
return err
}
err = store.Set(key, bz)
if err != nil {
if err := k.FeeAllowance.Set(ctx, collections.Join(grantee, granter), grant); err != nil {
return err
}
@ -108,9 +118,6 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr
// UpdateAllowance updates the existing grant.
func (k Keeper) UpdateAllowance(ctx context.Context, granter, grantee sdk.AccAddress, feeAllowance feegrant.FeeAllowanceI) error {
store := k.storeService.OpenKVStore(ctx)
key := feegrant.FeeAllowanceKey(granter, grantee)
_, err := k.getGrant(ctx, granter, grantee)
if err != nil {
return err
@ -121,13 +128,7 @@ func (k Keeper) UpdateAllowance(ctx context.Context, granter, grantee sdk.AccAdd
return err
}
bz, err := k.cdc.Marshal(&grant)
if err != nil {
return err
}
err = store.Set(key, bz)
if err != nil {
if err := k.FeeAllowance.Set(ctx, collections.Join(grantee, granter), grant); err != nil {
return err
}
@ -149,10 +150,7 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd
return err
}
store := k.storeService.OpenKVStore(ctx)
key := feegrant.FeeAllowanceKey(granter, grantee)
err = store.Delete(key)
if err != nil {
if err := k.FeeAllowance.Remove(ctx, collections.Join(grantee, granter)); err != nil {
return err
}
@ -162,7 +160,7 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd
}
if exp != nil {
if err := store.Delete(feegrant.FeeAllowancePrefixQueue(exp, feegrant.FeeAllowanceKey(grantee, granter)[1:])); err != nil {
if err := k.FeeAllowanceQueue.Remove(ctx, collections.Join3(*exp, grantee, granter)); err != nil {
return err
}
}
@ -181,7 +179,7 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd
// If there is none, it returns nil, nil.
// Returns an error on parsing issues
func (k Keeper) GetAllowance(ctx context.Context, granter, grantee sdk.AccAddress) (feegrant.FeeAllowanceI, error) {
grant, err := k.getGrant(ctx, granter, grantee)
grant, err := k.FeeAllowance.Get(ctx, collections.Join(grantee, granter))
if err != nil {
return nil, err
}
@ -191,22 +189,11 @@ func (k Keeper) GetAllowance(ctx context.Context, granter, grantee sdk.AccAddres
// getGrant returns entire grant between both accounts
func (k Keeper) getGrant(ctx context.Context, granter, grantee sdk.AccAddress) (*feegrant.Grant, error) {
store := k.storeService.OpenKVStore(ctx)
key := feegrant.FeeAllowanceKey(granter, grantee)
bz, err := store.Get(key)
feegrant, err := k.FeeAllowance.Get(ctx, collections.Join(grantee, granter))
if err != nil {
return nil, err
}
if len(bz) == 0 {
return nil, sdkerrors.ErrNotFound.Wrap("fee-grant not found")
}
var feegrant feegrant.Grant
if err := k.cdc.Unmarshal(bz, &feegrant); err != nil {
return nil, err
}
return &feegrant, nil
}
@ -218,14 +205,12 @@ func (k Keeper) IterateAllFeeAllowances(ctx context.Context, cb func(grant feegr
iter := storetypes.KVStorePrefixIterator(runtime.KVStoreAdapter(store), feegrant.FeeAllowanceKeyPrefix)
defer iter.Close()
stop := false
for ; iter.Valid() && !stop; iter.Next() {
bz := iter.Value()
var feeGrant feegrant.Grant
if err := k.cdc.Unmarshal(bz, &feeGrant); err != nil {
return err
}
stop = cb(feeGrant)
err := k.FeeAllowance.Walk(ctx, nil, func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (stop bool, err error) {
return cb(grant), nil
})
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
return err
}
return nil
@ -312,32 +297,28 @@ func (k Keeper) ExportGenesis(ctx context.Context) (*feegrant.GenesisState, erro
}, err
}
func (k Keeper) addToFeeAllowanceQueue(ctx context.Context, grantKey []byte, exp *time.Time) error {
store := k.storeService.OpenKVStore(ctx)
return store.Set(feegrant.FeeAllowancePrefixQueue(exp, grantKey), []byte{})
}
// RemoveExpiredAllowances iterates grantsByExpiryQueue and deletes the expired grants.
func (k Keeper) RemoveExpiredAllowances(ctx context.Context) error {
exp := sdk.UnwrapSDKContext(ctx).BlockTime()
store := k.storeService.OpenKVStore(ctx)
iterator, err := store.Iterator(feegrant.FeeAllowanceQueueKeyPrefix, storetypes.InclusiveEndBytes(feegrant.AllowanceByExpTimeKey(&exp)))
if err != nil {
rng := collections.NewPrefixUntilTripleRange[time.Time, sdk.AccAddress, sdk.AccAddress](exp)
err := k.FeeAllowanceQueue.Walk(ctx, rng, func(key collections.Triple[time.Time, sdk.AccAddress, sdk.AccAddress], value bool) (stop bool, err error) {
grantee, granter := key.K2(), key.K3()
if err := k.FeeAllowance.Remove(ctx, collections.Join(grantee, granter)); err != nil {
return true, err
}
if err := k.FeeAllowanceQueue.Remove(ctx, key); err != nil {
return true, err
}
return false, nil
})
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
return err
}
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
err = store.Delete(iterator.Key())
if err != nil {
return err
}
granter, grantee := feegrant.ParseAddressesFromFeeAllowanceQueueKey(iterator.Key())
err = store.Delete(feegrant.FeeAllowanceKey(granter, grantee))
if err != nil {
return err
}
}
return nil
}

View File

@ -291,7 +291,7 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
// verify: feegrant is revoked
_, err = suite.feegrantKeeper.GetAllowance(ctx, suite.addrs[0], suite.addrs[2])
suite.Error(err)
suite.Contains(err.Error(), "fee-grant not found")
suite.Contains(err.Error(), "not found")
}
func (suite *KeeperTestSuite) TestIterateGrants() {
@ -323,6 +323,7 @@ func (suite *KeeperTestSuite) TestIterateGrants() {
func (suite *KeeperTestSuite) TestPruneGrants() {
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
now := suite.ctx.BlockTime()
oneDay := now.AddDate(0, 0, 1)
oneYearExpiry := now.AddDate(1, 0, 0)
testCases := []struct {
@ -336,24 +337,35 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
postRun func()
}{
{
name: "grant not pruned from state",
ctx: suite.ctx,
granter: suite.addrs[0],
grantee: suite.addrs[1],
name: "grant pruned from state after a block: error",
ctx: suite.ctx,
granter: suite.addrs[0],
grantee: suite.addrs[1],
expErrMsg: "not found",
allowance: &feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &now,
},
},
{
name: "grant pruned from state after a block: error",
name: "grant not pruned from state before expiration: no error",
ctx: suite.ctx,
granter: suite.addrs[2],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneDay,
},
},
{
name: "grant pruned from state after a day: error",
ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 1)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
granter: suite.addrs[1],
grantee: suite.addrs[0],
expErrMsg: "not found",
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &now,
Expiration: &oneDay,
},
},
{
@ -368,7 +380,7 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
},
{
name: "grant pruned from state after a year: error",
ctx: suite.ctx.WithBlockTime(now.AddDate(1, 0, 1)),
ctx: suite.ctx.WithBlockTime(now.AddDate(1, 0, 0)),
granter: suite.addrs[1],
grantee: suite.addrs[2],
expErrMsg: "not found",
@ -384,7 +396,6 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
grantee: suite.addrs[2],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneYearExpiry,
},
},
}
@ -399,13 +410,16 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
suite.NoError(err)
err = suite.feegrantKeeper.RemoveExpiredAllowances(tc.ctx)
suite.NoError(err)
grant, err := suite.feegrantKeeper.GetAllowance(tc.ctx, tc.granter, tc.grantee)
if tc.expErrMsg != "" {
suite.Error(err)
suite.Contains(err.Error(), tc.expErrMsg)
} else {
suite.NoError(err)
suite.NotNil(grant)
}
if tc.postRun != nil {
tc.postRun()
}

View File

@ -227,7 +227,7 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() {
},
func() {},
true,
"fee-grant not found",
"not found",
},
{
"success: revoke fee allowance",
@ -269,7 +269,7 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() {
},
func() {},
true,
"fee-grant not found",
"not found",
},
}

View File

@ -1,10 +1,7 @@
package feegrant
import (
time "time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
"cosmossdk.io/collections"
)
const (
@ -24,72 +21,9 @@ const (
var (
// FeeAllowanceKeyPrefix is the set of the kvstore for fee allowance data
// - 0x00<allowance_key_bytes>: allowance
FeeAllowanceKeyPrefix = []byte{0x00}
FeeAllowanceKeyPrefix = collections.NewPrefix(0)
// FeeAllowanceQueueKeyPrefix is the set of the kvstore for fee allowance keys data
// - 0x01<allowance_prefix_queue_key_bytes>: <empty value>
FeeAllowanceQueueKeyPrefix = []byte{0x01}
FeeAllowanceQueueKeyPrefix = collections.NewPrefix(1)
)
// FeeAllowanceKey is the canonical key to store a grant from granter to grantee
// We store by grantee first to allow searching by everyone who granted to you
//
// Key format:
// - <0x00><len(grantee_address_bytes)><grantee_address_bytes><len(granter_address_bytes)><granter_address_bytes>
func FeeAllowanceKey(granter, grantee sdk.AccAddress) []byte {
return append(FeeAllowancePrefixByGrantee(grantee), address.MustLengthPrefix(granter.Bytes())...)
}
// FeeAllowancePrefixByGrantee returns a prefix to scan for all grants to this given address.
//
// Key format:
// - <0x00><len(grantee_address_bytes)><grantee_address_bytes>
func FeeAllowancePrefixByGrantee(grantee sdk.AccAddress) []byte {
return append(FeeAllowanceKeyPrefix, address.MustLengthPrefix(grantee.Bytes())...)
}
// FeeAllowancePrefixQueue is the canonical key to store grant key.
//
// Key format:
// - <0x01><exp_bytes><len(grantee_address_bytes)><grantee_address_bytes><len(granter_address_bytes)><granter_address_bytes>
func FeeAllowancePrefixQueue(exp *time.Time, key []byte) []byte {
allowanceByExpTimeKey := AllowanceByExpTimeKey(exp)
return append(allowanceByExpTimeKey, key...)
}
// AllowanceByExpTimeKey returns a key with `FeeAllowanceQueueKeyPrefix`, expiry
//
// Key format:
// - <0x01><exp_bytes>
func AllowanceByExpTimeKey(exp *time.Time) []byte {
// no need of appending len(exp_bytes) here, `FormatTimeBytes` gives const length everytime.
return append(FeeAllowanceQueueKeyPrefix, sdk.FormatTimeBytes(*exp)...)
}
// ParseAddressesFromFeeAllowanceKey extracts and returns the granter, grantee from the given key.
func ParseAddressesFromFeeAllowanceKey(key []byte) (granter, grantee []byte) {
// key is of format:
// 0x00<granteeAddressLen (1 Byte)><granteeAddress_Bytes><granterAddressLen (1 Byte)><granterAddress_Bytes>
granterAddrLen, granterAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, 1, 1) // ignore key[0] since it is a prefix key
grantee, granterAddrEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrLenEndIndex+1, int(granterAddrLen[0]))
granteeAddrLen, granteeAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrEndIndex+1, 1)
granter, _ = sdk.ParseLengthPrefixedBytes(key, granteeAddrLenEndIndex+1, int(granteeAddrLen[0]))
return granter, grantee
}
// ParseAddressesFromFeeAllowanceQueueKey extracts and returns the granter, grantee from the given key.
func ParseAddressesFromFeeAllowanceQueueKey(key []byte) (granter, grantee []byte) {
lenTime := len(sdk.FormatTimeBytes(time.Now()))
// key is of format:
// <0x01><expiration_bytes(fixed length)><granteeAddressLen (1 Byte)><granteeAddress_Bytes><granterAddressLen (1 Byte)><granterAddress_Bytes>
granterAddrLen, granterAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, 1+lenTime, 1) // ignore key[0] since it is a prefix key
grantee, granterAddrEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrLenEndIndex+1, int(granterAddrLen[0]))
granteeAddrLen, granteeAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrEndIndex+1, 1)
granter, _ = sdk.ParseLengthPrefixedBytes(key, granteeAddrLenEndIndex+1, int(granteeAddrLen[0]))
return granter, grantee
}

View File

@ -1,47 +0,0 @@
package feegrant_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"cosmossdk.io/x/feegrant"
codecaddress "github.com/cosmos/cosmos-sdk/codec/address"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestMarshalAndUnmarshalFeegrantKey(t *testing.T) {
addressCodec := codecaddress.NewBech32Codec("cosmos")
grantee, err := addressCodec.StringToBytes("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x")
require.NoError(t, err)
granter, err := addressCodec.StringToBytes("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts")
require.NoError(t, err)
key := feegrant.FeeAllowanceKey(granter, grantee)
require.Len(t, key, len(grantee)+len(granter)+3)
require.Equal(t, feegrant.FeeAllowancePrefixByGrantee(grantee), key[:len(grantee)+2])
g1, g2 := feegrant.ParseAddressesFromFeeAllowanceKey(key)
require.Equal(t, granter, g1)
require.Equal(t, grantee, g2)
}
func TestMarshalAndUnmarshalFeegrantKeyQueueKey(t *testing.T) {
addressCodec := codecaddress.NewBech32Codec("cosmos")
grantee, err := addressCodec.StringToBytes("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x")
require.NoError(t, err)
granter, err := addressCodec.StringToBytes("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts")
require.NoError(t, err)
exp := time.Now()
expBytes := sdk.FormatTimeBytes(exp)
key := feegrant.FeeAllowancePrefixQueue(&exp, feegrant.FeeAllowanceKey(granter, grantee)[1:])
require.Len(t, key, len(grantee)+len(granter)+3+len(expBytes))
granter1, grantee1 := feegrant.ParseAddressesFromFeeAllowanceQueueKey(key)
require.Equal(t, granter, granter1)
require.Equal(t, grantee, grantee1)
}

View File

@ -89,7 +89,7 @@ func TestFeegrantPruning(t *testing.T) {
})
require.NoError(t, err)
require.NotNil(t, res)
require.Len(t, res.Allowances, 3)
require.Len(t, res.Allowances, 2)
testCtx.Ctx = testCtx.Ctx.WithBlockTime(now.AddDate(0, 0, 2))
module.EndBlocker(testCtx.Ctx, feegrantKeeper)