github.com/Finschia/finschia-sdk@v0.49.1/x/auth/ante/setup.go (about)

     1  package ante
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/Finschia/finschia-sdk/types"
     7  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     8  	"github.com/Finschia/finschia-sdk/x/auth/legacy/legacytx"
     9  )
    10  
    11  var _ GasTx = (*legacytx.StdTx)(nil) // assert StdTx implements GasTx
    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, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be GasTx")
    38  	}
    39  
    40  	newCtx = SetGasMeter(simulate, ctx, gasTx.GetGas())
    41  
    42  	// Decorator will catch an OutOfGasPanic caused in the next antehandler
    43  	// AnteHandlers must have their own defer/recover in order for the BaseApp
    44  	// to know how much gas was used! This is because the GasMeter is created in
    45  	// the AnteHandler, but if it panics the context won't be set properly in
    46  	// runTx's recover call.
    47  	defer func() {
    48  		if r := recover(); r != nil {
    49  			switch rType := r.(type) {
    50  			case sdk.ErrorOutOfGas:
    51  				log := fmt.Sprintf(
    52  					"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
    53  					rType.Descriptor, gasTx.GetGas(), newCtx.GasMeter().GasConsumed())
    54  
    55  				err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log)
    56  			default:
    57  				panic(r)
    58  			}
    59  		}
    60  	}()
    61  
    62  	return next(newCtx, tx, simulate)
    63  }
    64  
    65  // SetGasMeter returns a new context with a gas meter set from a given context.
    66  func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit uint64) sdk.Context {
    67  	// In various cases such as simulation and during the genesis block, we do not
    68  	// meter any gas utilization.
    69  	if simulate || ctx.BlockHeight() == 0 {
    70  		return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
    71  	}
    72  
    73  	return ctx.WithGasMeter(sdk.NewGasMeter(gasLimit))
    74  }