191 lines
5.6 KiB
Go
191 lines
5.6 KiB
Go
|
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())
|
||
|
}
|