github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/auth/ante/fee.go (about)

     1  package ante
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported"
     8  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/keeper"
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    10  
    11  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    12  )
    13  
    14  var (
    15  	_ FeeTx = (*types.StdTx)(nil) // assert StdTx implements FeeTx
    16  )
    17  
    18  // FeeTx defines the interface to be implemented by Tx to use the FeeDecorators
    19  type FeeTx interface {
    20  	sdk.Tx
    21  	GetGas() uint64
    22  	GetFee() sdk.Coins
    23  	FeePayer(ctx sdk.Context) sdk.AccAddress
    24  }
    25  
    26  // MempoolFeeDecorator will check if the transaction's fee is at least as large
    27  // as the local validator's minimum gasFee (defined in validator config).
    28  // If fee is too low, decorator returns error and tx is rejected from mempool.
    29  // Note this only applies when ctx.CheckTx = true
    30  // If fee is high enough or not CheckTx, then call next AnteHandler
    31  // CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator
    32  type MempoolFeeDecorator struct{}
    33  
    34  func NewMempoolFeeDecorator() MempoolFeeDecorator {
    35  	return MempoolFeeDecorator{}
    36  }
    37  
    38  func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
    39  	feeTx, ok := tx.(FeeTx)
    40  	if !ok {
    41  		return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    42  	}
    43  	feeCoins := feeTx.GetFee()
    44  	gas := feeTx.GetGas()
    45  
    46  	// Ensure that the provided fees meet a minimum threshold for the validator,
    47  	// if this is a CheckTx. This is only for local mempool purposes, and thus
    48  	// is only ran on check tx.
    49  	if ctx.IsCheckTx() && !simulate {
    50  		minGasPrices := ctx.MinGasPrices()
    51  		if !minGasPrices.IsZero() {
    52  			requiredFees := make(sdk.Coins, len(minGasPrices))
    53  
    54  			// Determine the required fees by multiplying each required minimum gas
    55  			// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
    56  			glDec := sdk.NewDec(int64(gas))
    57  			for i, gp := range minGasPrices {
    58  				fee := gp.Amount.Mul(glDec)
    59  				requiredFees[i] = sdk.NewDecCoinFromDec(gp.Denom, fee)
    60  			}
    61  
    62  			if !feeCoins.IsAnyGTE(requiredFees) {
    63  				return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
    64  			}
    65  		}
    66  	}
    67  
    68  	return next(ctx, tx, simulate)
    69  }
    70  
    71  // DeductFeeDecorator deducts fees from the first signer of the tx
    72  // If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error
    73  // Call next AnteHandler if fees successfully deducted
    74  // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator
    75  type DeductFeeDecorator struct {
    76  	ak           keeper.AccountKeeper
    77  	supplyKeeper types.SupplyKeeper
    78  }
    79  
    80  func NewDeductFeeDecorator(ak keeper.AccountKeeper, sk types.SupplyKeeper) DeductFeeDecorator {
    81  	return DeductFeeDecorator{
    82  		ak:           ak,
    83  		supplyKeeper: sk,
    84  	}
    85  }
    86  
    87  func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
    88  	feeTx, ok := tx.(FeeTx)
    89  	if !ok {
    90  		return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
    91  	}
    92  
    93  	if addr := dfd.supplyKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil {
    94  		panic(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName))
    95  	}
    96  
    97  	feePayer := feeTx.FeePayer(ctx)
    98  	feePayerAcc := dfd.ak.GetAccount(ctx, feePayer)
    99  
   100  	if feePayerAcc == nil {
   101  		return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer)
   102  	}
   103  
   104  	// deduct the fees
   105  	if !feeTx.GetFee().IsZero() {
   106  		err = DeductFees(dfd.supplyKeeper, ctx, feePayerAcc, feeTx.GetFee())
   107  		if err != nil {
   108  			return ctx, err
   109  		}
   110  	}
   111  
   112  	return next(ctx, tx, simulate)
   113  }
   114  
   115  // DeductFees deducts fees from the given account.
   116  //
   117  // NOTE: We could use the BankKeeper (in addition to the AccountKeeper, because
   118  // the BankKeeper doesn't give us accounts), but it seems easier to do this.
   119  func DeductFees(supplyKeeper types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error {
   120  	blockTime := ctx.BlockTime()
   121  	coins := acc.GetCoins()
   122  
   123  	if !fees.IsValid() {
   124  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
   125  	}
   126  
   127  	// verify the account has enough funds to pay for fees
   128  	_, hasNeg := coins.SafeSub(fees)
   129  	if hasNeg {
   130  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds,
   131  			"insufficient funds to pay for fees; %s < %s", coins, fees)
   132  	}
   133  
   134  	// Validate the account has enough "spendable" coins as this will cover cases
   135  	// such as vesting accounts.
   136  	spendableCoins := acc.SpendableCoins(blockTime)
   137  	if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg {
   138  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds,
   139  			"insufficient funds to pay for fees; %s < %s", spendableCoins, fees)
   140  	}
   141  
   142  	err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
   143  	if err != nil {
   144  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
   145  	}
   146  
   147  	return nil
   148  }