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 }