Fix runTx Gas Consumption during Tx Aborting (#3244)
This commit is contained in:
parent
0e26a39f6f
commit
9eef341df9
|
@ -24,6 +24,8 @@ BREAKING CHANGES
|
||||||
* [stake] \#2513 Validator power type from Dec -> Int
|
* [stake] \#2513 Validator power type from Dec -> Int
|
||||||
* [stake] \#3233 key and value now contain duplicate fields to simplify code
|
* [stake] \#3233 key and value now contain duplicate fields to simplify code
|
||||||
* [\#3064](https://github.com/cosmos/cosmos-sdk/issues/3064) Sanitize `sdk.Coin` denom. Coins denoms are now case insensitive, i.e. 100fooToken equals to 100FOOTOKEN.
|
* [\#3064](https://github.com/cosmos/cosmos-sdk/issues/3064) Sanitize `sdk.Coin` denom. Coins denoms are now case insensitive, i.e. 100fooToken equals to 100FOOTOKEN.
|
||||||
|
* [\#3242](https://github.com/cosmos/cosmos-sdk/issues/3242) Fix infinite gas
|
||||||
|
meter utilization during aborted ante handler executions.
|
||||||
|
|
||||||
* Tendermint
|
* Tendermint
|
||||||
|
|
||||||
|
|
|
@ -503,6 +503,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
|
||||||
} else {
|
} else {
|
||||||
gasMeter = sdk.NewInfiniteGasMeter()
|
gasMeter = sdk.NewInfiniteGasMeter()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter)
|
app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter)
|
||||||
|
|
||||||
if app.beginBlocker != nil {
|
if app.beginBlocker != nil {
|
||||||
|
@ -732,7 +733,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||||
defer func() {
|
defer func() {
|
||||||
if mode == runTxModeDeliver {
|
if mode == runTxModeDeliver {
|
||||||
ctx.BlockGasMeter().ConsumeGas(
|
ctx.BlockGasMeter().ConsumeGas(
|
||||||
ctx.GasMeter().GasConsumedToLimit(), "block gas meter")
|
ctx.GasMeter().GasConsumedToLimit(),
|
||||||
|
"block gas meter",
|
||||||
|
)
|
||||||
|
|
||||||
if ctx.BlockGasMeter().GasConsumed() < startingGas {
|
if ctx.BlockGasMeter().GasConsumed() < startingGas {
|
||||||
panic(sdk.ErrorGasOverflow{"tx gas summation"})
|
panic(sdk.ErrorGasOverflow{"tx gas summation"})
|
||||||
}
|
}
|
||||||
|
@ -751,23 +755,29 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||||
|
|
||||||
// Cache wrap context before anteHandler call in case it aborts.
|
// Cache wrap context before anteHandler call in case it aborts.
|
||||||
// This is required for both CheckTx and DeliverTx.
|
// This is required for both CheckTx and DeliverTx.
|
||||||
// https://github.com/cosmos/cosmos-sdk/issues/2772
|
// Ref: https://github.com/cosmos/cosmos-sdk/issues/2772
|
||||||
|
//
|
||||||
// NOTE: Alternatively, we could require that anteHandler ensures that
|
// NOTE: Alternatively, we could require that anteHandler ensures that
|
||||||
// writes do not happen if aborted/failed. This may have some
|
// writes do not happen if aborted/failed. This may have some
|
||||||
// performance benefits, but it'll be more difficult to get right.
|
// performance benefits, but it'll be more difficult to get right.
|
||||||
anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
|
anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
|
||||||
|
|
||||||
newCtx, result, abort := app.anteHandler(anteCtx, tx, (mode == runTxModeSimulate))
|
newCtx, result, abort := app.anteHandler(anteCtx, tx, (mode == runTxModeSimulate))
|
||||||
|
if !newCtx.IsZero() {
|
||||||
|
// At this point, newCtx.MultiStore() is cache-wrapped, or something else
|
||||||
|
// replaced by the ante handler. We want the original multistore, not one
|
||||||
|
// which was cache-wrapped for the ante handler.
|
||||||
|
//
|
||||||
|
// Also, in the case of the tx aborting, we need to track gas consumed via
|
||||||
|
// the instantiated gas meter in the ante handler, so we update the context
|
||||||
|
// prior to returning.
|
||||||
|
ctx = newCtx.WithMultiStore(ms)
|
||||||
|
}
|
||||||
|
|
||||||
if abort {
|
if abort {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if !newCtx.IsZero() {
|
|
||||||
// At this point, newCtx.MultiStore() is cache wrapped,
|
|
||||||
// or something else replaced by anteHandler.
|
|
||||||
// We want the original ms, not one which was cache-wrapped
|
|
||||||
// for the ante handler.
|
|
||||||
ctx = newCtx.WithMultiStore(ms)
|
|
||||||
}
|
|
||||||
msCache.Write()
|
msCache.Write()
|
||||||
gasWanted = result.GasWanted
|
gasWanted = result.GasWanted
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,10 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
||||||
// all transactions must be of type auth.StdTx
|
// all transactions must be of type auth.StdTx
|
||||||
stdTx, ok := tx.(StdTx)
|
stdTx, ok := tx.(StdTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ctx, sdk.ErrInternal("tx must be StdTx").Result(), true
|
// Set a gas meter with limit 0 as to prevent an infinite gas meter attack
|
||||||
|
// during runTx.
|
||||||
|
newCtx = SetGasMeter(simulate, ctx, 0)
|
||||||
|
return newCtx, sdk.ErrInternal("tx must be StdTx").Result(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
params := ak.GetParams(ctx)
|
params := ak.GetParams(ctx)
|
||||||
|
@ -44,7 +47,7 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newCtx = SetGasMeter(simulate, ctx, stdTx)
|
newCtx = SetGasMeter(simulate, ctx, stdTx.Fee.Gas)
|
||||||
|
|
||||||
// AnteHandlers must have their own defer/recover in order for the BaseApp
|
// AnteHandlers must have their own defer/recover in order for the BaseApp
|
||||||
// to know how much gas was used! This is because the GasMeter is created in
|
// to know how much gas was used! This is because the GasMeter is created in
|
||||||
|
@ -306,14 +309,14 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGasMeter returns a new context with a gas meter set from a given context.
|
// SetGasMeter returns a new context with a gas meter set from a given context.
|
||||||
func SetGasMeter(simulate bool, ctx sdk.Context, stdTx StdTx) sdk.Context {
|
func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit uint64) sdk.Context {
|
||||||
// In various cases such as simulation and during the genesis block, we do not
|
// In various cases such as simulation and during the genesis block, we do not
|
||||||
// meter any gas utilization.
|
// meter any gas utilization.
|
||||||
if simulate || ctx.BlockHeight() == 0 {
|
if simulate || ctx.BlockHeight() == 0 {
|
||||||
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
|
return ctx.WithGasMeter(sdk.NewGasMeter(gasLimit))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignBytes returns a slice of bytes to sign over for a given transaction
|
// GetSignBytes returns a slice of bytes to sign over for a given transaction
|
||||||
|
|
Loading…
Reference in New Issue