Merge PR #2795: Enforce block maximum gas limit in DeliverTx

R4R: Enforce block maximum gas limit in DeliverTx
This commit is contained in:
frog power 4000 2018-11-26 00:00:43 -05:00 committed by GitHub
commit 8af2eb2b5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 354 additions and 56 deletions

4
Gopkg.lock generated
View File

@ -165,13 +165,12 @@
version = "v1.2.0"
[[projects]]
digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
@ -644,6 +643,7 @@
"github.com/bgentry/speakeasy",
"github.com/btcsuite/btcd/btcec",
"github.com/cosmos/go-bip39",
"github.com/gogo/protobuf/proto",
"github.com/golang/protobuf/proto",
"github.com/gorilla/mux",
"github.com/mattn/go-isatty",

View File

@ -43,7 +43,8 @@ FEATURES
* [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`.
* SDK
* [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time
* [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time
* [core] \#2775 Add deliverTx maximum block gas limit
* Tendermint

View File

@ -6,6 +6,7 @@ import (
"runtime/debug"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types"
@ -19,11 +20,8 @@ import (
"github.com/cosmos/cosmos-sdk/version"
)
// Key to store the header in the DB itself.
// Use the db directly instead of a store to avoid
// conflicts with handlers writing to the store
// and to avoid affecting the Merkle root.
var dbHeaderKey = []byte("header")
// Key to store the consensus params in the main store.
var mainConsensusParamsKey = []byte("consensus_params")
// Enum mode for app.runTx
type runTxMode uint8
@ -48,9 +46,11 @@ type BaseApp struct {
queryRouter QueryRouter // router for redirecting query calls
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
anteHandler sdk.AnteHandler // ante handler for fee and auth
// set upon LoadVersion or LoadLatestVersion.
mainKey *sdk.KVStoreKey // Main KVStore in cms
// may be nil
anteHandler sdk.AnteHandler // ante handler for fee and auth
initChainer sdk.InitChainer // initialize state with validators and state blob
beginBlocker sdk.BeginBlocker // logic to run before any txs
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
@ -66,7 +66,11 @@ type BaseApp struct {
deliverState *state // for DeliverTx
voteInfos []abci.VoteInfo // absent validators from begin block
// minimum fees for spam prevention
// consensus params
// TODO move this in the future to baseapp param store on main store.
consensusParams *abci.ConsensusParams
// spam prevention
minimumFees sdk.Coins
// flag for sealing
@ -77,10 +81,6 @@ var _ abci.Application = (*BaseApp)(nil)
// NewBaseApp returns a reference to an initialized BaseApp.
//
// TODO: Determine how to use a flexible and robust configuration paradigm that
// allows for sensible defaults while being highly configurable
// (e.g. functional options).
//
// NOTE: The db is used to store the version number for now.
// Accepts a user-defined txDecoder
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
@ -94,7 +94,6 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod
queryRouter: NewQueryRouter(),
txDecoder: txDecoder,
}
for _, option := range options {
option(app)
}
@ -137,21 +136,23 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
}
// load latest application version
func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error {
// panics if called more than once on a running baseapp
func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error {
err := app.cms.LoadLatestVersion()
if err != nil {
return err
}
return app.initFromStore(mainKey)
return app.initFromMainStore(mainKey)
}
// load application version
func (app *BaseApp) LoadVersion(version int64, mainKey sdk.StoreKey) error {
// panics if called more than once on a running baseapp
func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error {
err := app.cms.LoadVersion(version)
if err != nil {
return err
}
return app.initFromStore(mainKey)
return app.initFromMainStore(mainKey)
}
// the last CommitID of the multistore
@ -165,13 +166,34 @@ func (app *BaseApp) LastBlockHeight() int64 {
}
// initializes the remaining logic from app.cms
func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error {
// main store should exist.
// TODO: we don't actually need the main store here
main := app.cms.GetKVStore(mainKey)
if main == nil {
mainStore := app.cms.GetKVStore(mainKey)
if mainStore == nil {
return errors.New("baseapp expects MultiStore with 'main' KVStore")
}
// memoize mainKey
if app.mainKey != nil {
panic("app.mainKey expected to be nil; duplicate init?")
}
app.mainKey = mainKey
// load consensus params from the main store
consensusParamsBz := mainStore.Get(mainConsensusParamsKey)
if consensusParamsBz != nil {
var consensusParams = &abci.ConsensusParams{}
err := proto.Unmarshal(consensusParamsBz, consensusParams)
if err != nil {
panic(err)
}
app.setConsensusParams(consensusParams)
} else {
// It will get saved later during InitChain.
// TODO assert that InitChain hasn't yet been called.
}
// Needed for `gaiad export`, which inits from store but never calls initchain
app.setCheckState(abci.Header{})
@ -220,6 +242,29 @@ func (app *BaseApp) setDeliverState(header abci.Header) {
}
}
// setConsensusParams memoizes the consensus params.
func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) {
app.consensusParams = consensusParams
}
// setConsensusParams stores the consensus params to the main store.
func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) {
consensusParamsBz, err := proto.Marshal(consensusParams)
if err != nil {
panic(err)
}
mainStore := app.cms.GetKVStore(app.mainKey)
mainStore.Set(mainConsensusParamsKey, consensusParamsBz)
}
// getMaximumBlockGas gets the maximum gas from the consensus params.
func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) {
if app.consensusParams == nil || app.consensusParams.BlockSize == nil {
return 0
}
return uint64(app.consensusParams.BlockSize.MaxGas)
}
//______________________________________________________________________________
// ABCI
@ -242,8 +287,15 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
}
// Implements ABCI
// InitChain runs the initialization logic directly on the CommitMultiStore and commits it.
// InitChain runs the initialization logic directly on the CommitMultiStore.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// Stash the consensus params in the cms main store and memoize.
if req.ConsensusParams != nil {
app.setConsensusParams(req.ConsensusParams)
app.storeConsensusParams(req.ConsensusParams)
}
// Initialize the deliver state and check state with ChainID and run initChain
app.setDeliverState(abci.Header{ChainID: req.ChainId})
app.setCheckState(abci.Header{ChainID: req.ChainId})
@ -251,6 +303,11 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
if app.initChainer == nil {
return
}
// add block gas meter for any genesis transactions (allow infinite gas)
app.deliverState.ctx = app.deliverState.ctx.
WithBlockGasMeter(sdk.NewInfiniteGasMeter())
res = app.initChainer(app.deliverState.ctx, req)
// NOTE: we don't commit, but BeginBlock for block 1
@ -424,9 +481,20 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
} else {
// In the first block, app.deliverState.ctx will already be initialized
// by InitChain. Context is now updated with Header information.
app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header).WithBlockHeight(req.Header.Height)
app.deliverState.ctx = app.deliverState.ctx.
WithBlockHeader(req.Header).
WithBlockHeight(req.Header.Height)
}
// add block gas meter
var gasMeter sdk.GasMeter
if maxGas := app.getMaximumBlockGas(); maxGas > 0 {
gasMeter = sdk.NewGasMeter(maxGas)
} else {
gasMeter = sdk.NewInfiniteGasMeter()
}
app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter)
if app.beginBlocker != nil {
res = app.beginBlocker(app.deliverState.ctx, req)
}
@ -464,9 +532,10 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
// Implements ABCI
func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
// Decode the Tx.
var result sdk.Result
var tx, err = app.txDecoder(txBytes)
var result sdk.Result
if err != nil {
result = err.Result()
} else {
@ -617,6 +686,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
ctx := app.getContextForTx(mode, txBytes)
ms := ctx.MultiStore()
// only run the tx if there is block gas remaining
if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() {
result = sdk.ErrOutOfGas("no block gas left to run tx").Result()
return
}
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
@ -633,6 +708,18 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
result.GasUsed = ctx.GasMeter().GasConsumed()
}()
// If BlockGasMeter() panics it will be caught by the above recover and
// return an error - in any case BlockGasMeter will consume gas past
// the limit.
// NOTE: this must exist in a separate defer function for the
// above recovery to recover from this one
defer func() {
if mode == runTxModeDeliver {
ctx.BlockGasMeter().ConsumeGas(
ctx.GasMeter().GasConsumedToLimit(), "block gas meter")
}
}()
var msgs = tx.GetMsgs()
if err := validateBasicTxMsgs(msgs); err != nil {
return err.Result()
@ -704,14 +791,6 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
// Implements ABCI
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
header := app.deliverState.ctx.BlockHeader()
/*
// Write the latest Header to the store
headerBytes, err := proto.Marshal(&header)
if err != nil {
panic(err)
}
app.db.SetSync(dbHeaderKey, headerBytes)
*/
// Write the Deliver state and commit the MultiStore
app.deliverState.ms.Write()

View File

@ -56,7 +56,9 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp {
require.Equal(t, t.Name(), app.Name())
// no stores are mounted
require.Panics(t, func() { app.LoadLatestVersion(capKey1) })
require.Panics(t, func() {
app.LoadLatestVersion(capKey1)
})
app.MountStoresIAVL(capKey1, capKey2)
@ -514,6 +516,7 @@ func TestDeliverTx(t *testing.T) {
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.InitChain(abci.RequestInitChain{})
// Create same codec used in txDecoder
codec := codec.New()
@ -853,6 +856,110 @@ func TestTxGasLimits(t *testing.T) {
}
}
// Test that transactions exceeding gas limits fail
func TestMaxBlockGasLimits(t *testing.T) {
gasGranted := uint64(10)
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
// NOTE/TODO/XXX:
// AnteHandlers must have their own defer/recover in order
// for the BaseApp to know how much gas was used used!
// This is because the GasMeter is created in the AnteHandler,
// but if it panics the context won't be set properly in runTx's recover ...
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasGranted
res.GasUsed = newCtx.GasMeter().GasConsumed()
default:
panic(r)
}
}
}()
count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante")
res = sdk.Result{
GasWanted: gasGranted,
}
return
})
}
routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
count := msg.(msgCounter).Counter
ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
return sdk.Result{}
})
}
app := setupBaseApp(t, anteOpt, routerOpt)
app.InitChain(abci.RequestInitChain{
ConsensusParams: &abci.ConsensusParams{
BlockSize: &abci.BlockSizeParams{
MaxGas: 100,
},
},
})
testCases := []struct {
tx *txTest
numDelivers int
gasUsedPerDeliver uint64
fail bool
failAfterDeliver int
}{
{newTxCounter(0, 0), 0, 0, false, 0},
{newTxCounter(9, 1), 2, 10, false, 0},
{newTxCounter(10, 0), 3, 10, false, 0},
{newTxCounter(10, 0), 10, 10, false, 0},
{newTxCounter(2, 7), 11, 9, false, 0},
{newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass
{newTxCounter(10, 0), 11, 10, true, 10},
{newTxCounter(10, 0), 15, 10, true, 10},
{newTxCounter(9, 0), 12, 9, true, 11}, // fly past the limit
}
for i, tc := range testCases {
fmt.Printf("debug i: %v\n", i)
tx := tc.tx
// reset the block gas
app.BeginBlock(abci.RequestBeginBlock{})
// execute the transaction multiple times
for j := 0; j < tc.numDelivers; j++ {
res := app.Deliver(tx)
ctx := app.getState(runTxModeDeliver).ctx
blockGasUsed := ctx.BlockGasMeter().GasConsumed()
// check for failed transactions
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().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().IsPastLimit())
}
}
}
}
func TestBaseAppAnteHandler(t *testing.T) {
anteKey := []byte("ante-key")
anteOpt := func(bapp *BaseApp) {

View File

@ -41,8 +41,10 @@ type GenesisState struct {
GenTxs []json.RawMessage `json:"gentxs"`
}
func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState {
func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState,
stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState,
slashingData slashing.GenesisState) GenesisState {
return GenesisState{
Accounts: accounts,
@ -260,7 +262,7 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm
"account %v not in genesis.json: %+v", addr, addrMap)
}
if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) {
err = fmt.Errorf("insufficient fund for the delegation: %s < %s",
err = fmt.Errorf("insufficient fund for the delegation: %v < %v",
acc.Coins.AmountOf(msg.Delegation.Denom), msg.Delegation.Amount)
}

View File

@ -62,8 +62,9 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application
}
func exportAppStateAndTMValidators(
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64,
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64) (
json.RawMessage, []tmtypes.GenesisValidator, error) {
gApp := app.NewGaiaApp(logger, db, traceStore)
if height != -1 {
err := gApp.LoadHeight(height)

View File

@ -61,3 +61,64 @@ persisted even when the following Handler processing logic fails.
It is possible that a malicious proposer may include a transaction in a block
that fails the AnteHandler. In this case, all state transitions for the
offending transaction are discarded.
## Other ABCI Messages
Besides `CheckTx` and `DeliverTx`, BaseApp handles the following ABCI messages.
### Info
TODO complete description
### SetOption
TODO complete description
### Query
TODO complete description
### InitChain
TODO complete description
During chain initialization InitChain runs the initialization logic directly on
the CommitMultiStore. The deliver and check states are initialized with the
ChainID.
Note that we do not commit after InitChain, so BeginBlock for block 1 starts
from the deliver state as initialized by InitChain.
### BeginBlock
TODO complete description
### EndBlock
TODO complete description
### Commit
TODO complete description
## Gas Management
### Gas: InitChain
During InitChain, the block gas meter is initialized with an infinite amount of
gas to run any genesis transactions.
Additionally, the InitChain request message includes ConsensusParams as
declared in the genesis.json file.
### Gas: BeginBlock
The block gas meter is reset during BeginBlock for the deliver state. If no
maximum block gas is set within baseapp then an infinite gas meter is set,
otherwise a gas meter with `ConsensusParam.BlockSize.MaxGas` is initialized.
### Gas: DeliverTx
Before the transaction logic is run, the `BlockGasMeter` is first checked to
see if any gas remains. If no gas remains, then `DeliverTx` immediately returns
an error.
After the transaction has been processed, the used gas (up to the transaction
gas limit) is deducted from the BlockGasMeter. If the remaining gas exceeds the
meter's limits, then DeliverTx returns an error and the transaction is not
committed.

View File

@ -230,13 +230,19 @@ func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore {
}
// Implements MultiStore.
// If the store does not exist, panics.
func (rs *rootMultiStore) GetStore(key StoreKey) Store {
return rs.stores[key]
store := rs.stores[key]
if store == nil {
panic("Could not load store " + key.String())
}
return store
}
// GetKVStore implements the MultiStore interface. If tracing is enabled on the
// rootMultiStore, a wrapped TraceKVStore will be returned with the given
// tracer, otherwise, the original KVStore will be returned.
// If the store does not exist, panics.
func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore {
store := rs.stores[key].(KVStore)

View File

@ -133,13 +133,13 @@ const (
contextKeyMultiStore contextKey = iota
contextKeyBlockHeader
contextKeyBlockHeight
contextKeyConsensusParams
contextKeyChainID
contextKeyIsCheckTx
contextKeyTxBytes
contextKeyLogger
contextKeyVoteInfos
contextKeyGasMeter
contextKeyBlockGasMeter
contextKeyMinimumFees
)
@ -151,10 +151,6 @@ func (c Context) BlockHeader() abci.Header { return c.Value(contextKeyBlockHeade
func (c Context) BlockHeight() int64 { return c.Value(contextKeyBlockHeight).(int64) }
func (c Context) ConsensusParams() abci.ConsensusParams {
return c.Value(contextKeyConsensusParams).(abci.ConsensusParams)
}
func (c Context) ChainID() string { return c.Value(contextKeyChainID).(string) }
func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) }
@ -167,6 +163,8 @@ func (c Context) VoteInfos() []abci.VoteInfo {
func (c Context) GasMeter() GasMeter { return c.Value(contextKeyGasMeter).(GasMeter) }
func (c Context) BlockGasMeter() GasMeter { return c.Value(contextKeyBlockGasMeter).(GasMeter) }
func (c Context) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) }
func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) }
@ -198,16 +196,6 @@ func (c Context) WithBlockHeight(height int64) Context {
return c.withValue(contextKeyBlockHeight, height).withValue(contextKeyBlockHeader, newHeader)
}
func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context {
if params == nil {
return c
}
// TODO: Do we need to handle invalid MaxGas values?
return c.withValue(contextKeyConsensusParams, params).
WithGasMeter(NewGasMeter(uint64(params.BlockSize.MaxGas)))
}
func (c Context) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) }
func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contextKeyTxBytes, txBytes) }
@ -220,6 +208,10 @@ func (c Context) WithVoteInfos(VoteInfos []abci.VoteInfo) Context {
func (c Context) WithGasMeter(meter GasMeter) Context { return c.withValue(contextKeyGasMeter, meter) }
func (c Context) WithBlockGasMeter(meter GasMeter) Context {
return c.withValue(contextKeyBlockGasMeter, meter)
}
func (c Context) WithIsCheckTx(isCheckTx bool) Context {
return c.withValue(contextKeyIsCheckTx, isCheckTx)
}

View File

@ -34,7 +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)
IsPastLimit() bool
IsOutOfGas() bool
}
type basicGasMeter struct {
@ -54,6 +58,17 @@ func (g *basicGasMeter) GasConsumed() Gas {
return g.consumed
}
func (g *basicGasMeter) Limit() Gas {
return g.limit
}
func (g *basicGasMeter) GasConsumedToLimit() Gas {
if g.IsPastLimit() {
return g.limit
}
return g.consumed
}
func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) {
var overflow bool
@ -68,6 +83,14 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) {
}
}
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
}
@ -83,6 +106,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
@ -93,6 +124,14 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) {
}
}
func (g *infiniteGasMeter) IsPastLimit() bool {
return false
}
func (g *infiniteGasMeter) IsOutOfGas() bool {
return false
}
// GasConfig defines gas cost for each operation on KVStores
type GasConfig struct {
HasCost Gas

View File

@ -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
}

View File

@ -64,6 +64,7 @@ type MultiStore interface { //nolint
CacheMultiStore() CacheMultiStore
// Convenience for fetching substores.
// If the store does not exist, panics.
GetStore(StoreKey) Store
GetKVStore(StoreKey) KVStore