diff --git a/x/logic/keeper/grpc_query_ask.go b/x/logic/keeper/grpc_query_ask.go index 219e3b84..ed22524f 100644 --- a/x/logic/keeper/grpc_query_ask.go +++ b/x/logic/keeper/grpc_query_ask.go @@ -33,6 +33,13 @@ func (k Keeper) Ask(ctx goctx.Context, req *types.QueryServiceAskRequest) (respo panic(r) } + if sdkCtx.GasMeter().IsOutOfGas() { + response, err = nil, sdkerrors.Wrapf( + types.LimitExceeded, "out of gas: %s (%d/%d)", + types.ModuleName, sdkCtx.GasMeter().GasConsumed(), sdkCtx.GasMeter().Limit()) + + return + } }() sdkCtx.GasMeter().ConsumeGas(sdkCtx.GasMeter().GasConsumed(), types.ModuleName) diff --git a/x/logic/types/gas.go b/x/logic/types/gas.go index cbad9384..5952bab7 100644 --- a/x/logic/types/gas.go +++ b/x/logic/types/gas.go @@ -1,6 +1,7 @@ package types import ( + "runtime" "sync" sdk "github.com/cosmos/cosmos-sdk/types" @@ -18,6 +19,20 @@ type safeGasMeter struct { func (m *safeGasMeter) ConsumeGas(amount uint64, descriptor string) { m.mutex.Lock() + defer func() { + if r := recover(); r != nil { + if _, ok := r.(sdk.ErrorOutOfGas); ok { + // Since predicate is called into a goroutine, when out of gas is thrown, the main caller + // (grpc: https://github.com/okp4/okp4d/blob/main/x/logic/keeper/grpc_query_ask.go#L25-L36, or querier) + // cannot recover ErrOutOfGas. To avoid the chain panicking, we need to exit without panic. + // Goexit runs all deferred calls before terminating the goroutine. Because Goexit + // is not a panic, any recover calls in those deferred functions will return nil. + // This is a temporary solution before implementing a context cancellation. + runtime.Goexit() + } + panic(r) + } + }() defer m.mutex.Unlock() m.gasMeter.ConsumeGas(amount, descriptor)