From 4afd53d81b515f4c3b0ac4faf6a2590fe1fcf3e6 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 20:07:30 -0800 Subject: [PATCH] Consume block gas to tx gas limit even upon overconsumption --- baseapp/baseapp.go | 14 +++++++------- baseapp/baseapp_test.go | 6 +++--- types/gas.go | 37 ++++++++++++++++++++++++++++++++++--- types/gas_test.go | 9 +++++++++ 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 19a5d6dab..136099d0d 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -688,7 +688,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.initializeContext(ctx, mode) // only run the tx if there is block gas remaining - if mode == runTxModeDeliver && ctx.BlockGasMeter().PastLimit() { + if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() { result = sdk.ErrOutOfGas("no block gas left to run tx").Result() return } @@ -705,6 +705,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk } } + // consume block gas whether panic or not. + if mode == runTxModeDeliver { + ctx.BlockGasMeter().ConsumeGas( + ctx.GasMeter().GasConsumedToLimit(), "block gas meter") + } + result.GasWanted = gasWanted result.GasUsed = ctx.GasMeter().GasConsumed() }() @@ -750,12 +756,6 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result = app.runMsgs(runMsgCtx, msgs, mode) result.GasWanted = gasWanted - // consume block gas - if mode == runTxModeDeliver { - ctx.BlockGasMeter().ConsumeGas( - ctx.GasMeter().GasConsumed(), "block gas meter") - } - // only update state if all messages pass if result.IsOK() { msCache.Write() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 83cecee08..3dd997150 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -943,16 +943,16 @@ func TestMaxBlockGasLimits(t *testing.T) { if tc.fail && (j+1) > tc.failAfterDeliver { require.Equal(t, res.Code, sdk.CodeOutOfGas, fmt.Sprintf("%d: %v, %v", i, tc, res)) require.Equal(t, res.Codespace, sdk.CodespaceRoot, fmt.Sprintf("%d: %v, %v", i, tc, res)) - require.True(t, ctx.BlockGasMeter().PastLimit()) + //require.True(t, ctx.BlockGasMeter().IsPastLimit()) NOTE: not necessarily true. + require.True(t, ctx.BlockGasMeter().IsOutOfGas()) } else { - // check gas used and wanted expBlockGasUsed := tc.gasUsedPerDeliver * uint64(j+1) require.Equal(t, expBlockGasUsed, blockGasUsed, fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res)) require.True(t, res.IsOK(), fmt.Sprintf("%d,%d: %v, %v", i, j, tc, res)) - require.False(t, ctx.BlockGasMeter().PastLimit()) + require.False(t, ctx.BlockGasMeter().IsPastLimit()) } } } diff --git a/types/gas.go b/types/gas.go index d9bd7de40..be9a55b77 100644 --- a/types/gas.go +++ b/types/gas.go @@ -34,8 +34,11 @@ type ErrorGasOverflow struct { // GasMeter interface to track gas consumption type GasMeter interface { GasConsumed() Gas + GasConsumedToLimit() Gas + Limit() Gas ConsumeGas(amount Gas, descriptor string) - PastLimit() bool + IsPastLimit() bool + IsOutOfGas() bool } type basicGasMeter struct { @@ -55,6 +58,18 @@ func (g *basicGasMeter) GasConsumed() Gas { return g.consumed } +func (g *basicGasMeter) Limit() Gas { + return g.limit +} + +func (g *basicGasMeter) GasConsumedToLimit() Gas { + if g.consumed > g.limit { + return g.limit + } else { + return g.consumed + } +} + func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool @@ -69,10 +84,14 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { } } -func (g *basicGasMeter) PastLimit() bool { +func (g *basicGasMeter) IsPastLimit() bool { return g.consumed > g.limit } +func (g *basicGasMeter) IsOutOfGas() bool { + return g.consumed >= g.limit +} + type infiniteGasMeter struct { consumed Gas } @@ -88,6 +107,14 @@ func (g *infiniteGasMeter) GasConsumed() Gas { return g.consumed } +func (g *infiniteGasMeter) GasConsumedToLimit() Gas { + return g.consumed +} + +func (g *infiniteGasMeter) Limit() Gas { + return 0 +} + func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool @@ -98,7 +125,11 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { } } -func (g *infiniteGasMeter) PastLimit() bool { +func (g *infiniteGasMeter) IsPastLimit() bool { + return false +} + +func (g *infiniteGasMeter) IsOutOfGas() bool { return false } diff --git a/types/gas_test.go b/types/gas_test.go index f4452053f..5f862dccd 100644 --- a/types/gas_test.go +++ b/types/gas_test.go @@ -27,9 +27,18 @@ func TestGasMeter(t *testing.T) { used += usage require.NotPanics(t, func() { meter.ConsumeGas(usage, "") }, "Not exceeded limit but panicked. tc #%d, usage #%d", tcnum, unum) require.Equal(t, used, meter.GasConsumed(), "Gas consumption not match. tc #%d, usage #%d", tcnum, unum) + require.Equal(t, used, meter.GasConsumedToLimit(), "Gas consumption (to limit) not match. tc #%d, usage #%d", tcnum, unum) + require.False(t, meter.IsPastLimit(), "Not exceeded limit but got IsPastLimit() true") + if unum < len(tc.usage)-1 { + require.False(t, meter.IsOutOfGas(), "Not yet at limit but got IsOutOfGas() true") + } else { + require.True(t, meter.IsOutOfGas(), "At limit but got IsOutOfGas() false") + } } require.Panics(t, func() { meter.ConsumeGas(1, "") }, "Exceeded but not panicked. tc #%d", tcnum) + require.Equal(t, meter.GasConsumedToLimit(), meter.Limit(), "Gas consumption (to limit) not match limit") + require.Equal(t, meter.GasConsumed(), meter.Limit()+1, "Gas consumption not match limit+1") break }