cosmos-sdk/x/feegrant/keeper/keeper.go

191 lines
5.6 KiB
Go
Raw Normal View History

Add fee grant module (#8061) * Add docs * Add BasicFeeAllowance implementation * Add expiration structs and complete basic fee * Add delegation messages, add validation logic * Add keeper and helper structs * Add alias and handler to top level * Add delegation module * Add basic querier * Add types tests * Add types tests * More internal test coverage * Solid internal test coverage * Expose Querier to top level module * Add FeeAccount to auth/types, like StdTx, SignDoc * Fix all tests in x/auth * All tests pass * Appease the Golang Linter * Add fee-account command line flag * Start on DelegatedDeductFeeDecorator * Cleanup the Decorator * Wire up delegation module in simapp * add basic test for decorator (no delegation) * Table tests for deduct fees * Table tests over all conditions of delegated fee decorator * Build full ante handler stack and test it * Start genesis * Implement Genesis * Rename package delegation to subkeys * Clarify antes test cases, handle empty account w/o fees * Allow paying delegated fees with no account * Pull mempool into delegated ante, for control on StdFee * Use custom DelegatedTx, DelegatedFee for subkeys * Revert all changes to x/auth.StdTx * Appease scopelint * Register DelegatedTx with codec * Address PR comments * Remove unnecessary DelegatedMempoolFeeDecorator * Cleaned up errors in querier * Clean up message sign bytes * Minor PR comments * Replace GetAllFees... with Iterator variants * PrepareForExport adjusts grant expiration height * Panic on de/serialization error in keeper * Move custom ante handler chain to tests, update docs * More cleanup * More doc cleanup * Renamed subkeys module to fee_grant * Rename subkeys/delegation to fee grant in all strings * Modify Msg and Keeper methods to use Grant not Delegate * Add PeriodicFeeAllowance * Update aliases * Cover all accept cases for PeriodicFeeAllowance * Et tu scopelint? * Update docs as requested * Remove error return from GetFeeGrant * Code cleanup as requested by PR * Updated all errors to use new sdk/errors package * Use test suite for keeper tests * Clean up alias.go file * Define expected interfaces in exported, rather than importing from account * Remove dependency on auth/ante * Improve godoc, Logger * Cleaned up ExpiresAt * Improve error reporting with UseGrantedFee * Enforce period limit subset of basic limit * Add events * Rename fee_grant to feegrant * Ensure KeeperTestSuite actually runs * Move types/tx to types * Update alias file, include ante * I do need nolint in alias.go * Properly emit events in the handler. Use cosmos-sdk in amino types * Update godoc * Linting... * Update errors * Update pkg doc and fix ante-handler order * Merge PR #5782: Migrate x/feegrant to proto * fix errors * proto changes * proto changes * fix errors * fix errors * genesis state changed to proto * fix keeper tests * fix test * fixed tests * fix tests * updated expected keepers * updated ante tests * lint * deleted alias.go * tx updated to proto tx * remove explicit signmode * tests * Added `cli/query.go` * Added tx.go in cli * updated `module.go` * resolve errors in tx.go * Add fee payer gentx func * updated tx * fixed error * WIP: cli tests * fix query error * fix tests * Unused types and funcs * fix tests * rename helper func to create tx * remove unused * update tx cfg * fix cli tests * added simulations * Add `decoder.go` * fix build fail * added init genesis code * update tx.go * fixed LGTM alert * modified cli * remove gogoproto extensions * change acc address type to string * lint * fix simulations * Add gen simulations * remove legacy querier * remove legacy code * add grpc queries tests * fix simulations * update module.go * lint * register feegrant NewSimulationManager * fix sims * fix sims * add genesis test * add periodic grant * updated cmd * changed times * updated flags * removed days as period clock * added condition for period and exp * add periodic fee cli tests * udpated tests * fix lint * fix tests * fix sims * renaming to `fee_grant` * review changes * fix test * add condition for duplicate grants * fix tests * add `genTxWithFeeGranter` in tests * fix simulation * one of changes & test fixes * fix test * fix lint * changed package name `feegrant` to `fee_grant` * review comments * review changes * review change * review changes * added fee-account in flags * address review changes * read fee granter from cli * updated create account with mnemonic * Address review comments * move `simapp/ante` file to `feegrant/ante` * update keeper logic to create account * update docs * fix tests * update `serviceMsgClientConn` from `msgservice` * review changes * add test case for using more fees than allowed * eliminate panic checks from keeper * fix lint * change store keys string to bytes * fix tests * review changes * review changes * udpate docs * make spend limit optional * fix tests * fix tests * review changes * add norace tag * proto-docs * add docs Co-authored-by: Ethan Frey <ethanfrey@users.noreply.github.com> Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Aleksandr Bezobchuk <aleks.bezobchuk@gmail.com> Co-authored-by: SaReN <sahithnarahari@gmail.com> Co-authored-by: aleem1413 <aleem@vitwit.com> Co-authored-by: MD Aleem <72057206+aleem1314@users.noreply.github.com> Co-authored-by: Anil Kumar Kammari <anil@vitwit.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2021-01-29 11:54:51 -08:00
package keeper
import (
"fmt"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
)
// 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.BinaryMarshaler
storeKey sdk.StoreKey
authKeeper types.AccountKeeper
}
// NewKeeper creates a fee grant Keeper
func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
authKeeper: ak,
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// GrantFeeAllowance creates a new grant
func (k Keeper) GrantFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance types.FeeAllowanceI) error {
// create the account if it is not in account state
granteeAcc := k.authKeeper.GetAccount(ctx, grantee)
if granteeAcc == nil {
granteeAcc = k.authKeeper.NewAccountWithAddress(ctx, grantee)
k.authKeeper.SetAccount(ctx, granteeAcc)
}
store := ctx.KVStore(k.storeKey)
key := types.FeeAllowanceKey(granter, grantee)
grant, err := types.NewFeeAllowanceGrant(granter, grantee, feeAllowance)
if err != nil {
return err
}
bz, err := k.cdc.MarshalBinaryBare(&grant)
if err != nil {
return err
}
store.Set(key, bz)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSetFeeGrant,
sdk.NewAttribute(types.AttributeKeyGranter, grant.Granter),
sdk.NewAttribute(types.AttributeKeyGrantee, grant.Grantee),
),
)
return nil
}
// RevokeFeeAllowance removes an existing grant
func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) error {
store := ctx.KVStore(k.storeKey)
key := types.FeeAllowanceKey(granter, grantee)
_, found := k.GetFeeGrant(ctx, granter, grantee)
if !found {
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "fee-grant not found")
}
store.Delete(key)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeRevokeFeeGrant,
sdk.NewAttribute(types.AttributeKeyGranter, granter.String()),
sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()),
),
)
return nil
}
// GetFeeAllowance returns the allowance between the granter and grantee.
// If there is none, it returns nil, nil.
// Returns an error on parsing issues
func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) types.FeeAllowanceI {
grant, found := k.GetFeeGrant(ctx, granter, grantee)
if !found {
return nil
}
return grant.GetFeeGrant()
}
// GetFeeGrant returns entire grant between both accounts
func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, bool) {
store := ctx.KVStore(k.storeKey)
key := types.FeeAllowanceKey(granter, grantee)
bz := store.Get(key)
if len(bz) == 0 {
return types.FeeAllowanceGrant{}, false
}
var feegrant types.FeeAllowanceGrant
k.cdc.MustUnmarshalBinaryBare(bz, &feegrant)
return feegrant, true
}
// IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee.
// Callback to get all data, returns true to stop, false to keep reading
func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress, cb func(types.FeeAllowanceGrant) bool) error {
store := ctx.KVStore(k.storeKey)
prefix := types.FeeAllowancePrefixByGrantee(grantee)
iter := sdk.KVStorePrefixIterator(store, prefix)
defer iter.Close()
stop := false
for ; iter.Valid() && !stop; iter.Next() {
bz := iter.Value()
var feeGrant types.FeeAllowanceGrant
k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant)
stop = cb(feeGrant)
}
return nil
}
// IterateAllFeeAllowances iterates over all the grants in the store.
// Callback to get all data, returns true to stop, false to keep reading
// Calling this without pagination is very expensive and only designed for export genesis
func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowanceGrant) bool) error {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, types.FeeAllowanceKeyPrefix)
defer iter.Close()
stop := false
for ; iter.Valid() && !stop; iter.Next() {
bz := iter.Value()
var feeGrant types.FeeAllowanceGrant
k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant)
stop = cb(feeGrant)
}
return nil
}
// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee
func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error {
grant, found := k.GetFeeGrant(ctx, granter, grantee)
if !found || grant.GetFeeGrant() == nil {
return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing")
}
remove, err := grant.GetFeeGrant().Accept(fee, ctx.BlockTime(), ctx.BlockHeight())
if err == nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeUseFeeGrant,
sdk.NewAttribute(types.AttributeKeyGranter, granter.String()),
sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()),
),
)
}
if remove {
k.RevokeFeeAllowance(ctx, granter, grantee)
// note this returns nil if err == nil
return sdkerrors.Wrap(err, "removed grant")
}
if err != nil {
return sdkerrors.Wrap(err, "invalid grant")
}
// if we accepted, store the updated state of the allowance
return k.GrantFeeAllowance(ctx, granter, grantee, grant.GetFeeGrant())
}