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

chore: (x/feegrant) update keeper logic to do not allow duplicate grant #14294

Merged
merged 14 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/auth/vesting) [#13502](https://github.com/cosmos/cosmos-sdk/pull/13502) Add Amino Msg registration for `MsgCreatePeriodicVestingAccount`.
* (x/auth)[#13780](https://github.com/cosmos/cosmos-sdk/pull/13780) `id` (type of int64) in `AccountAddressByID` grpc query is now deprecated, update to account-id(type of uint64) to use `AccountAddressByID`.
* (x/group) [#13876](https://github.com/cosmos/cosmos-sdk/pull/13876) Fix group MinExecutionPeriod that is checked on execution now, instead of voting period end.
* (x/feegrant) [#14294](https://github.com/cosmos/cosmos-sdk/pull/14294) moved the logic of rejecting duplicate grant from `msg_server` to `keeper` method.
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
* (store) [#14378](https://github.com/cosmos/cosmos-sdk/pull/14378) The `CacheKV` store is thread-safe again, which includes improved iteration and deletion logic. Iteration is on a strictly isolated view now, which is breaking from previous behavior.

### API Breaking Changes
Expand Down
62 changes: 14 additions & 48 deletions x/feegrant/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {

// GrantAllowance creates a new grant
func (k Keeper) GrantAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance feegrant.FeeAllowanceI) error {
// Checking for duplicate entry
if f, _ := k.GetAllowance(ctx, granter, grantee); f != nil {

Check warning

Code scanning / gosec

Returned error is not propagated up the stack.

Returned error is not propagated up the stack.
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists")
}

// create the account if it is not in account state
granteeAcc := k.authKeeper.GetAccount(ctx, grantee)
if granteeAcc == nil {
Expand All @@ -51,54 +56,21 @@ 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, granter, grantee)

// If we didn't find any grant, we don't return any error.
// All other kinds of errors are returned.
if err != nil && !sdkerrors.IsOf(err, sdkerrors.ErrNotFound) {
exp, err := feeAllowance.ExpiresAt()
if err != nil {
Comment on lines +59 to +60
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought the PR was only about moving the duplicate entry check from msg_server to keeper.

Can you explain what these changes are about?

Copy link
Contributor

Choose a reason for hiding this comment

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

up

Copy link
Contributor Author

@atheeshp atheeshp Jan 3, 2023

Choose a reason for hiding this comment

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

Yes, the removed logic is for handling duplicate grant (previously we are updating the expiration with the newer grant's expiration for the duplicate grant), which is now unreachable if duplicate grant found.

return err
}

if 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()
switch {
case err != nil:
return err

case newExp != nil && newExp.Before(ctx.BlockTime()):
// expiration shouldn't be in the past.
if exp != nil && exp.Before(ctx.BlockTime()) {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "expiration is before current block time")
}

case 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)

case 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:])

case oldExp != nil && newExp != nil && !oldExp.Equal(*newExp):
// 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.

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

// add the new key to queue.
k.addToFeeAllowanceQueue(ctx, key[1:], newExp)
// remove the 1st byte and reuse the remaining key as it is
k.addToFeeAllowanceQueue(ctx, key[1:], exp)
}

grant, err := feegrant.NewGrant(granter, grantee, feeAllowance)
Expand Down Expand Up @@ -312,12 +284,6 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (*feegrant.GenesisState, error) {
}, 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{})
Expand Down
128 changes: 34 additions & 94 deletions x/feegrant/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,35 @@ func (suite *KeeperTestSuite) TestKeeperCrud() {
}

// let's set up some initial state here

// addrs[0] -> addrs[1] (basic)
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[1], basic)
suite.Require().NoError(err)

// addrs[0] -> addrs[2] (basic2)
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[2], basic2)
suite.Require().NoError(err)

// addrs[1] -> addrs[2] (basic)
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[1], suite.addrs[2], basic)
suite.Require().NoError(err)

// addrs[1] -> addrs[3] (basic)
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[1], suite.addrs[3], basic)
suite.Require().NoError(err)

// addrs[3] -> addrs[0] (basic2)
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[3], suite.addrs[0], basic2)
suite.Require().NoError(err)

// addrs[3] -> addrs[0] (basic2) expect error with duplicate grant
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[3], suite.addrs[0], basic2)
suite.Require().Error(err)

// remove some, overwrite other
_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[1].String()})
suite.Require().NoError(err)

_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[2].String()})
suite.Require().NoError(err)

Expand All @@ -101,6 +112,10 @@ func (suite *KeeperTestSuite) TestKeeperCrud() {
err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[2], basic)
suite.Require().NoError(err)

// revoke an existing grant and grant again with different allowance.
_, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[1].String(), Grantee: suite.addrs[2].String()})
suite.Require().NoError(err)

err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[1], suite.addrs[2], basic3)
suite.Require().NoError(err)

Expand Down Expand Up @@ -188,34 +203,50 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
fee sdk.Coins
allowed bool
final feegrant.FeeAllowanceI
postRun func()
}{
"use entire pot": {
granter: suite.addrs[0],
grantee: suite.addrs[1],
fee: suite.atom,
allowed: true,
final: nil,
postRun: func() {},
},
"too high": {
granter: suite.addrs[0],
grantee: suite.addrs[1],
fee: hugeAtom,
allowed: false,
final: future,
postRun: func() {
_, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{
Granter: suite.addrs[0].String(),
Grantee: suite.addrs[1].String(),
})
suite.Require().NoError(err)
},
},
"use a little": {
granter: suite.addrs[0],
grantee: suite.addrs[1],
fee: smallAtom,
allowed: true,
final: futureAfterSmall,
postRun: func() {
_, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{
Granter: suite.addrs[0].String(),
Grantee: suite.addrs[1].String(),
})
suite.Require().NoError(err)
},
},
}

for name, tc := range cases {
tc := tc
suite.Run(name, func() {
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[1], future)
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, tc.granter, tc.grantee, future)
suite.Require().NoError(err)

err = suite.feegrantKeeper.UseGrantedFees(suite.ctx, tc.granter, tc.grantee, tc.fee, []sdk.Msg{})
Expand All @@ -227,6 +258,8 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {

loaded, _ := suite.feegrantKeeper.GetAllowance(suite.ctx, tc.granter, tc.grantee)
suite.Equal(tc.final, loaded)

tc.postRun()
})
}

Expand Down Expand Up @@ -281,7 +314,6 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
now := suite.ctx.BlockTime()
oneYearExpiry := now.AddDate(1, 0, 0)
oneDay := now.AddDate(0, 0, 1)

testCases := []struct {
name string
Expand Down Expand Up @@ -345,98 +377,6 @@ func (suite *KeeperTestSuite) TestPruneGrants() {
Expiration: &oneYearExpiry,
},
},
{
name: "grant created with a day expiry & overwritten with no expiry shouldn't be pruned: no error",
ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 2)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
},
preRun: func() {
// create a grant with a day expiry.
allowance := &feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &oneDay,
}
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance)
suite.NoError(err)
},
postRun: func() {
_, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{
Granter: suite.addrs[2].String(),
Grantee: suite.addrs[1].String(),
})
suite.NoError(err)
},
},
{
name: "grant created with a day expiry & overwritten with a year expiry shouldn't be pruned: no error",
ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 2)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneYearExpiry,
},
preRun: func() {
// create a grant with a day expiry.
allowance := &feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &oneDay,
}
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance)
suite.NoError(err)
},
postRun: func() {
_, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{
Granter: suite.addrs[2].String(),
Grantee: suite.addrs[1].String(),
})
suite.NoError(err)
},
},
{
name: "grant created with a year expiry & overwritten with a day expiry should be pruned after a day: error",
ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 2)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneDay,
},
preRun: func() {
// create a grant with a year expiry.
allowance := &feegrant.BasicAllowance{
SpendLimit: suite.atom,
Expiration: &oneYearExpiry,
}
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance)
suite.NoError(err)
},
postRun: func() {},
expErrMsg: "not found",
},
{
name: "grant created with no expiry & overwritten with a day expiry should be pruned after a day: error",
ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 2)),
granter: suite.addrs[2],
grantee: suite.addrs[1],
allowance: &feegrant.BasicAllowance{
SpendLimit: eth,
Expiration: &oneDay,
},
preRun: func() {
// create a grant with no expiry.
allowance := &feegrant.BasicAllowance{
SpendLimit: suite.atom,
}
err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance)
suite.NoError(err)
},
postRun: func() {},
expErrMsg: "not found",
},
}

for _, tc := range testCases {
Expand Down
6 changes: 0 additions & 6 deletions x/feegrant/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

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

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/feegrant"
)

Expand Down Expand Up @@ -37,11 +36,6 @@ func (k msgServer) GrantAllowance(goCtx context.Context, msg *feegrant.MsgGrantA
return nil, err
}

// Checking for duplicate entry
if f, _ := k.Keeper.GetAllowance(ctx, granter, grantee); f != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists")
}

allowance, err := msg.GetFeeAllowanceI()
if err != nil {
return nil, err
Expand Down