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 }