github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/ante/setup.go (about)

     1  package ante
     2  
     3  import (
     4  	"fmt"
     5  
     6  	errorsmod "cosmossdk.io/errors"
     7  	storetypes "cosmossdk.io/store/types"
     8  
     9  	sdk "github.com/cosmos/cosmos-sdk/types"
    10  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    11  )
    12  
    13  // GasTx defines a Tx with a GetGas() method which is needed to use SetUpContextDecorator
    14  type GasTx interface {
    15  	sdk.Tx
    16  	GetGas() uint64
    17  }
    18  
    19  // SetUpContextDecorator sets the GasMeter in the Context and wraps the next AnteHandler with a defer clause
    20  // to recover from any downstream OutOfGas panics in the AnteHandler chain to return an error with information
    21  // on gas provided and gas used.
    22  // CONTRACT: Must be first decorator in the chain
    23  // CONTRACT: Tx must implement GasTx interface
    24  type SetUpContextDecorator struct{}
    25  
    26  func NewSetUpContextDecorator() SetUpContextDecorator {
    27  	return SetUpContextDecorator{}
    28  }
    29  
    30  func (sud SetUpContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
    31  	// all transactions must implement GasTx
    32  	gasTx, ok := tx.(GasTx)
    33  	if !ok {
    34  		// Set a gas meter with limit 0 as to prevent an infinite gas meter attack
    35  		// during runTx.
    36  		newCtx = SetGasMeter(simulate, ctx, 0)
    37  		return newCtx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be GasTx")
    38  	}
    39  
    40  	newCtx = SetGasMeter(simulate, ctx, gasTx.GetGas())
    41  
    42  	if cp := ctx.ConsensusParams(); cp.Block != nil {
    43  		// If there exists a maximum block gas limit, we must ensure that the tx
    44  		// does not exceed it.
    45  		if cp.Block.MaxGas > 0 && gasTx.GetGas() > uint64(cp.Block.MaxGas) {
    46  			return newCtx, errorsmod.Wrapf(sdkerrors.ErrInvalidGasLimit, "tx gas limit %d exceeds block max gas %d", gasTx.GetGas(), cp.Block.MaxGas)
    47  		}
    48  	}
    49  
    50  	// Decorator will catch an OutOfGasPanic caused in the next antehandler
    51  	// AnteHandlers must have their own defer/recover in order for the BaseApp
    52  	// to know how much gas was used! This is because the GasMeter is created in
    53  	// the AnteHandler, but if it panics the context won't be set properly in
    54  	// runTx's recover call.
    55  	defer func() {
    56  		if r := recover(); r != nil {
    57  			switch rType := r.(type) {
    58  			case storetypes.ErrorOutOfGas:
    59  				log := fmt.Sprintf(
    60  					"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
    61  					rType.Descriptor, gasTx.GetGas(), newCtx.GasMeter().GasConsumed())
    62  
    63  				err = errorsmod.Wrap(sdkerrors.ErrOutOfGas, log)
    64  			default:
    65  				panic(r)
    66  			}
    67  		}
    68  	}()
    69  
    70  	return next(newCtx, tx, simulate)
    71  }
    72  
    73  // SetGasMeter returns a new context with a gas meter set from a given context.
    74  func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit uint64) sdk.Context {
    75  	// In various cases such as simulation and during the genesis block, we do not
    76  	// meter any gas utilization.
    77  	if simulate || ctx.BlockHeight() == 0 {
    78  		return ctx.WithGasMeter(storetypes.NewInfiniteGasMeter())
    79  	}
    80  
    81  	return ctx.WithGasMeter(storetypes.NewGasMeter(gasLimit))
    82  }