diff --git a/x/interchaintxs/keeper/ibc_handlers.go b/x/interchaintxs/keeper/ibc_handlers.go index 7644b0447..01253cdba 100644 --- a/x/interchaintxs/keeper/ibc_handlers.go +++ b/x/interchaintxs/keeper/ibc_handlers.go @@ -34,28 +34,46 @@ func (k *Keeper) outOfGasRecovery( k.Logger(ctx).Debug("Out of gas", "Gas meter", gasMeter.String()) k.contractManagerKeeper.AddContractFailure(ctx, packet.SourceChannel, senderAddress.String(), packet.GetSequence(), failureAckType) - // FIXME: add distribution call } } -func (k *Keeper) createCachedContext(ctx sdk.Context) (cacheCtx sdk.Context, writeFn func(), newGasMeter sdk.GasMeter) { - gasLeft := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumed() +// createCachedContext creates a cached context for handling Sudo calls to CosmWasm smart-contracts. +// If there is an error during Sudo call, we can safely revert changes made in cached context. +func (k *Keeper) createCachedContext(ctx sdk.Context) (sdk.Context, func(), sdk.GasMeter) { + gasMeter := ctx.GasMeter() + // determines type of gas meter by its prefix: + // * BasicGasMeter - basic gas meter which is used for processing tx directly in block; + // * InfiniteGasMeter - is used to process txs during simulation calls. We don't need to create a limit for such meter, + // since it's infinite. + gasMeterIsLimited := strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") + + cacheCtx, writeFn := ctx.CacheContext() + + // if gas meter is limited: + // 1. calculate how much free gas left we have for a Sudo call; + // 2. If gasLeft less than reserved gas (GasReserved), we set gas limit for cached context to zero, meaning we can't + // process Sudo call; + // 3. If we have more gas left than reserved gas (GasReserved) for Sudo call, we set gas limit for cached context to + // difference between gas left and reserved gas: (gasLeft - GasReserve); + // + // GasReserve is the amount of gas on the context gas meter we need to reserve in order to add contract failure to keeper + // and process failed Sudo call + if gasMeterIsLimited { + gasLeft := gasMeter.Limit() - gasMeter.GasConsumed() + + var newLimit uint64 + if gasLeft < GasReserve { + newLimit = 0 + } else { + newLimit = gasLeft - GasReserve + } - var newLimit uint64 - if gasLeft < GasReserve { - newLimit = 0 - } else { - newLimit = gasLeft - GasReserve + gasMeter = sdk.NewGasMeter(newLimit) } - newGasMeter = sdk.NewGasMeter(newLimit) - - cacheCtx, writeFn = ctx.CacheContext() - if strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") { - cacheCtx = ctx.WithGasMeter(newGasMeter) - } + cacheCtx = cacheCtx.WithGasMeter(gasMeter) - return + return cacheCtx, writeFn, gasMeter } // HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call. diff --git a/x/transfer/ibc_handlers.go b/x/transfer/ibc_handlers.go index ddc4eb51a..9ac40d335 100644 --- a/x/transfer/ibc_handlers.go +++ b/x/transfer/ibc_handlers.go @@ -37,24 +37,43 @@ func (im IBCModule) outOfGasRecovery( } } -func (im IBCModule) createCachedContext(ctx sdk.Context) (cacheCtx sdk.Context, writeFn func(), newGasMeter sdk.GasMeter) { - gasLeft := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumed() +// createCachedContext creates a cached context for handling Sudo calls to CosmWasm smart-contracts. +// If there is an error during Sudo call, we can safely revert changes made in cached context. +func (im *IBCModule) createCachedContext(ctx sdk.Context) (sdk.Context, func(), sdk.GasMeter) { + gasMeter := ctx.GasMeter() + // determines type of gas meter by its prefix: + // * BasicGasMeter - basic gas meter which is used for processing tx directly in block; + // * InfiniteGasMeter - is used to process txs during simulation calls. We don't need to create a limit for such meter, + // since it's infinite. + gasMeterIsLimited := strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") + + cacheCtx, writeFn := ctx.CacheContext() + + // if gas meter is limited: + // 1. calculate how much free gas left we have for a Sudo call; + // 2. If gasLeft less than reserved gas (GasReserved), we set gas limit for cached context to zero, meaning we can't + // process Sudo call; + // 3. If we have more gas left than reserved gas (GasReserved) for Sudo call, we set gas limit for cached context to + // difference between gas left and reserved gas: (gasLeft - GasReserve); + // + // GasReserve is the amount of gas on the context gas meter we need to reserve in order to add contract failure to keeper + // and process failed Sudo call + if gasMeterIsLimited { + gasLeft := gasMeter.Limit() - gasMeter.GasConsumed() + + var newLimit uint64 + if gasLeft < GasReserve { + newLimit = 0 + } else { + newLimit = gasLeft - GasReserve + } - var newLimit uint64 - if gasLeft < GasReserve { - newLimit = 0 - } else { - newLimit = gasLeft - GasReserve + gasMeter = sdk.NewGasMeter(newLimit) } - newGasMeter = sdk.NewGasMeter(newLimit) - - cacheCtx, writeFn = ctx.CacheContext() - if strings.HasPrefix(ctx.GasMeter().String(), "BasicGasMeter") { - cacheCtx = ctx.WithGasMeter(newGasMeter) - } + cacheCtx = cacheCtx.WithGasMeter(gasMeter) - return + return cacheCtx, writeFn, gasMeter } // HandleAcknowledgement passes the acknowledgement data to the appropriate contract via a Sudo call.