From 46f9445f06c72582660512070d451d6e4b3878f5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 19:13:46 +0200 Subject: [PATCH 01/32] Add gas limit / gas consumed to context --- types/context.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/types/context.go b/types/context.go index e28523ebeba6..b3f30a2e0a0a 100644 --- a/types/context.go +++ b/types/context.go @@ -43,6 +43,8 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byt c = c.WithIsCheckTx(isCheckTx) c = c.WithTxBytes(txBytes) c = c.WithLogger(logger) + c = c.WithGasLimit(0) + c = c.WithGasConsumed(0) return c } @@ -127,6 +129,8 @@ const ( contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger + contextKeyGasLimit + contextKeyGasConsumed ) // NOTE: Do not expose MultiStore. @@ -155,6 +159,12 @@ func (c Context) TxBytes() []byte { func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) } +func (c Context) GasLimit() uint64 { + return c.Value(contextKeyGasLimit).(uint64) +} +func (c Context) GasConsumed() uint64 { + return c.Value(contextKeyGasConsumed).(uint64) +} func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) } @@ -177,6 +187,12 @@ func (c Context) WithTxBytes(txBytes []byte) Context { func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) } +func (c Context) WithGasLimit(limit uint64) Context { + return c.withValue(contextKeyGasLimit, limit) +} +func (c Context) WithGasConsumed(consumed uint64) Context { + return c.withValue(contextKeyGasConsumed, consumed) +} // Cache the multistore and return a new cached context. The cached context is // written to the context when writeCache is called. From 26991803ee50456719def16c3284437bdcf90200 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 19:48:12 +0200 Subject: [PATCH 02/32] GasMeter & context updates --- baseapp/baseapp.go | 10 +++--- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/handler_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- .../democoin/x/simplestake/keeper_test.go | 4 +-- types/context.go | 22 ++++-------- types/context_test.go | 4 +-- types/gas.go | 36 +++++++++++++++++++ types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +++--- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/bank/keeper_test.go | 6 ++-- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 15 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 types/gas.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index d3bf85fe8167..36843b77627c 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -35,6 +35,7 @@ type BaseApp struct { // must be set txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth + txGasLimit sdk.Gas // per-transaction gas limit // may be nil initChainer sdk.InitChainer // initialize state with validators and state blob @@ -66,6 +67,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), + txGasLimit: sdk.Gas(10000), } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -210,9 +212,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, app.txGasLimit) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, app.txGasLimit) } type state struct { @@ -228,7 +230,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger, app.txGasLimit), } } @@ -236,7 +238,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger, app.txGasLimit), } } diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index d497dee6996d..10e958ce7508 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 0) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 30aeafa2a566..48e9c53e7689 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index a4afb876a94b..324cdcdceb9b 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 302a2e58b6b0..1d19a61d7894 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/types/context.go b/types/context.go index b3f30a2e0a0a..33713d3533e7 100644 --- a/types/context.go +++ b/types/context.go @@ -30,7 +30,7 @@ type Context struct { } // create a new context -func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context { +func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger, gasLimit Gas) Context { c := Context{ Context: context.Background(), pst: newThePast(), @@ -43,8 +43,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byt c = c.WithIsCheckTx(isCheckTx) c = c.WithTxBytes(txBytes) c = c.WithLogger(logger) - c = c.WithGasLimit(0) - c = c.WithGasConsumed(0) + c = c.WithGasMeter(NewGasMeter(gasLimit)) return c } @@ -129,8 +128,7 @@ const ( contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger - contextKeyGasLimit - contextKeyGasConsumed + contextKeyGasMeter ) // NOTE: Do not expose MultiStore. @@ -159,11 +157,8 @@ func (c Context) TxBytes() []byte { func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) } -func (c Context) GasLimit() uint64 { - return c.Value(contextKeyGasLimit).(uint64) -} -func (c Context) GasConsumed() uint64 { - return c.Value(contextKeyGasConsumed).(uint64) +func (c Context) GasMeter() GasMeter { + return c.Value(contextKeyGasMeter).(GasMeter) } func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) @@ -187,11 +182,8 @@ func (c Context) WithTxBytes(txBytes []byte) Context { func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) } -func (c Context) WithGasLimit(limit uint64) Context { - return c.withValue(contextKeyGasLimit, limit) -} -func (c Context) WithGasConsumed(consumed uint64) Context { - return c.withValue(contextKeyGasConsumed, consumed) +func (c Context) WithGasMeter(meter GasMeter) Context { + return c.withValue(contextKeyGasMeter, meter) } // Cache the multistore and return a new cached context. The cached context is diff --git a/types/context_test.go b/types/context_test.go index ec5faab440f3..9eafed6259dd 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) return ctx } diff --git a/types/gas.go b/types/gas.go new file mode 100644 index 000000000000..63aa71c1b08d --- /dev/null +++ b/types/gas.go @@ -0,0 +1,36 @@ +package types + +import () + +type Gas uint64 + +type GasMeter interface { + GasExceeded() bool + ConsumeGas(amount Gas) + ConsumeGasOrFail(amount Gas) bool +} + +type basicGasMeter struct { + limit Gas + consumed Gas +} + +func NewGasMeter(limit Gas) GasMeter { + return &basicGasMeter{ + limit: limit, + consumed: 0, + } +} + +func (g *basicGasMeter) GasExceeded() bool { + return g.consumed > g.limit +} + +func (g *basicGasMeter) ConsumeGas(amount Gas) { + g.consumed += amount +} + +func (g *basicGasMeter) ConsumeGasOrFail(amount Gas) bool { + g.ConsumeGas(amount) + return g.GasExceeded() +} diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index e1759b06ac89..ab6fb605a16a 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index ec296b12beba..8310d30699bd 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index 89e318e0a1db..996a06889865 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index cdd418990a9c..80f673f996ad 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 3db16c5f92fd..324152df2611 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -117,7 +117,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -186,7 +186,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 60cc59bad9f8..298b5b46200c 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 27acebe0862a..15c300d41ce4 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 0) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From efc7843fb926f88180c2297b0eb854e988d97b4e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:28:53 +0200 Subject: [PATCH 03/32] Changes to bank keeper for gas --- types/errors.go | 6 ++ x/bank/keeper.go | 32 ++++++--- x/bank/keeper_test.go | 149 +++++++++++++++++++++++++++-------------- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 5 files changed, 128 insertions(+), 63 deletions(-) diff --git a/types/errors.go b/types/errors.go index 059a0dd749d9..20d4524644ce 100644 --- a/types/errors.go +++ b/types/errors.go @@ -52,6 +52,7 @@ const ( CodeUnknownAddress CodeType = 9 CodeInsufficientCoins CodeType = 10 CodeInvalidCoins CodeType = 11 + CodeOutOfGas CodeType = 12 // CodespaceRoot is a codespace for error codes in this file only. // Notice that 0 is an "unset" codespace, which can be overridden with @@ -88,6 +89,8 @@ func CodeToDefaultMsg(code CodeType) string { return "Insufficient coins" case CodeInvalidCoins: return "Invalid coins" + case CodeOutOfGas: + return "Out of gas" default: return fmt.Sprintf("Unknown code %d", code) } @@ -131,6 +134,9 @@ func ErrInsufficientCoins(msg string) Error { func ErrInvalidCoins(msg string) Error { return newErrorWithRootCodespace(CodeInvalidCoins, msg) } +func ErrOutOfGas(msg string) Error { + return newErrorWithRootCodespace(CodeOutOfGas, msg) +} //---------------------------------------- // Error & sdkError diff --git a/x/bank/keeper.go b/x/bank/keeper.go index d23167c3c52b..8bcbb2c86766 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -17,7 +17,7 @@ func NewKeeper(am sdk.AccountMapper) Keeper { } // GetCoins returns the coins at the addr. -func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } @@ -27,7 +27,7 @@ func (keeper Keeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } @@ -64,12 +64,12 @@ func NewSendKeeper(am sdk.AccountMapper) SendKeeper { } // GetCoins returns the coins at the addr. -func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } @@ -96,23 +96,26 @@ func NewViewKeeper(am sdk.AccountMapper) ViewKeeper { } // GetCoins returns the coins at the addr. -func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } //______________________________________________________________________________________________ -func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { +func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coins, sdk.Error) { + if ctx.GasMeter().ConsumeGasOrFail(10) { + return sdk.Coins{}, sdk.ErrOutOfGas("out of gas in getCoins") + } acc := am.GetAccount(ctx, addr) if acc == nil { - return sdk.Coins{} + return sdk.Coins{}, nil } - return acc.GetCoins() + return acc.GetCoins(), nil } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { @@ -126,8 +129,15 @@ func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.C } // HasCoins returns whether or not an account has at least amt coins. -func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool { - return getCoins(ctx, am, addr).IsGTE(amt) +func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { + if ctx.GasMeter().ConsumeGasOrFail(10) { + return false, sdk.ErrOutOfGas("out of gas in hasCoins") + } + coins, err := getCoins(ctx, am, addr) + if err != nil { + return false, err + } + return coins.IsGTE(amt), nil } // SubtractCoins subtracts amt from the coins at the addr. diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 324152df2611..430b87c7369b 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -42,58 +42,79 @@ func TestKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := coinKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) // Test AddCoins coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 25}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) // Test SubtractCoins coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) - _, _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) + _, err = coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) assert.Implements(t, (*sdk.Error)(nil), err) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 15}})) + bar, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}}) + assert.False(t, bar) // Test SendCoins coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -105,9 +126,12 @@ func TestKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } coinKeeper.InputOutputCoins(ctx, inputs, outputs) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + coins, err = coinKeeper.GetCoins(ctx, addr3) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -117,7 +141,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -129,40 +153,55 @@ func TestSendKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := sendKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) // Test SendCoins sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) sendKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -174,9 +213,12 @@ func TestSendKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } sendKeeper.InputOutputCoins(ctx, inputs, outputs) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + coins, err = sendKeeper.GetCoins(ctx, addr3) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -186,7 +228,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) @@ -196,14 +238,21 @@ func TestViewKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := viewKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = viewKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) } diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 298b5b46200c..e05245ff3878 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 15c300d41ce4..5c1c3872322c 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 10000) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From f0e4d24ea31a6def10387b40e1651f3a9a63e566 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:40:33 +0200 Subject: [PATCH 04/32] Basic gas impl, quick testcase --- examples/democoin/x/pow/handler_test.go | 2 +- x/bank/keeper.go | 3 +++ x/bank/keeper_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 48e9c53e7689..df83125025bb 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 8bcbb2c86766..efb2f01092a1 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -119,6 +119,9 @@ func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coin } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { + if ctx.GasMeter().ConsumeGasOrFail(100) { + return sdk.ErrOutOfGas("out of gas in setCoins") + } acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 430b87c7369b..a711a893c4fa 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -135,6 +135,31 @@ func TestKeeper(t *testing.T) { } +func TestKeeperGas(t *testing.T) { + ms, authKey := setupMultiStore() + + cdc := wire.NewCodec() + auth.RegisterBaseAccount(cdc) + + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10) + accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) + coinKeeper := NewKeeper(accountMapper) + + addr := sdk.Address([]byte("addr1")) + acc := accountMapper.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + accountMapper.SetAccount(ctx, acc) + coins, err := coinKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.NotNil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) +} + func TestSendKeeper(t *testing.T) { ms, authKey := setupMultiStore() From ddb3b36b7b2c513c284267845d2cdadba1327306 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:49:34 +0200 Subject: [PATCH 05/32] Pass gas consumed back in result struct --- baseapp/baseapp.go | 4 ++++ types/gas.go | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 36843b77627c..aeb534d43ce3 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -322,6 +322,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { Data: result.Data, Log: result.Log, GasWanted: result.GasWanted, + GasUsed: result.GasUsed, Fee: cmn.KI64Pair{ []byte(result.FeeDenom), result.FeeAmount, @@ -433,6 +434,9 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk result = handler(ctx, msg) + // Set gas utilized + result.GasUsed = ctx.GasMeter().GasConsumed() + // If result was successful, write to app.checkState.ms or app.deliverState.ms if result.IsOK() { msCache.Write() diff --git a/types/gas.go b/types/gas.go index 63aa71c1b08d..d4b63597c906 100644 --- a/types/gas.go +++ b/types/gas.go @@ -2,10 +2,11 @@ package types import () -type Gas uint64 +type Gas = int64 type GasMeter interface { GasExceeded() bool + GasConsumed() Gas ConsumeGas(amount Gas) ConsumeGasOrFail(amount Gas) bool } @@ -26,6 +27,10 @@ func (g *basicGasMeter) GasExceeded() bool { return g.consumed > g.limit } +func (g *basicGasMeter) GasConsumed() Gas { + return g.consumed +} + func (g *basicGasMeter) ConsumeGas(amount Gas) { g.consumed += amount } From af379b6cf660d5e7359a73eff233846fd55ced9c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 21:49:11 +0200 Subject: [PATCH 06/32] Linter fixes --- types/gas.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/gas.go b/types/gas.go index d4b63597c906..24be99f3fd95 100644 --- a/types/gas.go +++ b/types/gas.go @@ -2,8 +2,10 @@ package types import () +// Gas measured by the SDK type Gas = int64 +// GasMeter interface to track gas consumption type GasMeter interface { GasExceeded() bool GasConsumed() Gas From 1f8ef62d28b444bb39f1b7d24199bda77a2ca68a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:34:09 +0200 Subject: [PATCH 07/32] Swap to panic/recover version --- types/gas.go | 23 +++--- x/bank/keeper.go | 40 ++++------ x/bank/keeper_test.go | 170 ++++++++++++------------------------------ 3 files changed, 74 insertions(+), 159 deletions(-) diff --git a/types/gas.go b/types/gas.go index 24be99f3fd95..78246be2d1d1 100644 --- a/types/gas.go +++ b/types/gas.go @@ -5,12 +5,15 @@ import () // Gas measured by the SDK type Gas = int64 +// Error thrown when out of gas +type ErrorOutOfGas struct { + Descriptor string +} + // GasMeter interface to track gas consumption type GasMeter interface { - GasExceeded() bool GasConsumed() Gas - ConsumeGas(amount Gas) - ConsumeGasOrFail(amount Gas) bool + ConsumeGas(amount Gas, descriptor string) } type basicGasMeter struct { @@ -25,19 +28,13 @@ func NewGasMeter(limit Gas) GasMeter { } } -func (g *basicGasMeter) GasExceeded() bool { - return g.consumed > g.limit -} - func (g *basicGasMeter) GasConsumed() Gas { return g.consumed } -func (g *basicGasMeter) ConsumeGas(amount Gas) { +func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { g.consumed += amount -} - -func (g *basicGasMeter) ConsumeGasOrFail(amount Gas) bool { - g.ConsumeGas(amount) - return g.GasExceeded() + if g.consumed > g.limit { + panic(ErrorOutOfGas{descriptor}) + } } diff --git a/x/bank/keeper.go b/x/bank/keeper.go index efb2f01092a1..129ffa79230b 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -17,7 +17,7 @@ func NewKeeper(am sdk.AccountMapper) Keeper { } // GetCoins returns the coins at the addr. -func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } @@ -27,7 +27,7 @@ func (keeper Keeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } @@ -64,12 +64,12 @@ func NewSendKeeper(am sdk.AccountMapper) SendKeeper { } // GetCoins returns the coins at the addr. -func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } @@ -96,32 +96,28 @@ func NewViewKeeper(am sdk.AccountMapper) ViewKeeper { } // GetCoins returns the coins at the addr. -func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } //______________________________________________________________________________________________ -func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coins, sdk.Error) { - if ctx.GasMeter().ConsumeGasOrFail(10) { - return sdk.Coins{}, sdk.ErrOutOfGas("out of gas in getCoins") - } +func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { + ctx.GasMeter().ConsumeGas(10, "getCoins") acc := am.GetAccount(ctx, addr) if acc == nil { - return sdk.Coins{}, nil + return sdk.Coins{} } - return acc.GetCoins(), nil + return acc.GetCoins() } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { - if ctx.GasMeter().ConsumeGasOrFail(100) { - return sdk.ErrOutOfGas("out of gas in setCoins") - } + ctx.GasMeter().ConsumeGas(100, "setCoins") acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) @@ -132,19 +128,14 @@ func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.C } // HasCoins returns whether or not an account has at least amt coins. -func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { - if ctx.GasMeter().ConsumeGasOrFail(10) { - return false, sdk.ErrOutOfGas("out of gas in hasCoins") - } - coins, err := getCoins(ctx, am, addr) - if err != nil { - return false, err - } - return coins.IsGTE(amt), nil +func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool { + ctx.GasMeter().ConsumeGas(10, "hasCoins") + return getCoins(ctx, am, addr).IsGTE(amt) } // SubtractCoins subtracts amt from the coins at the addr. func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { + ctx.GasMeter().ConsumeGas(10, "subtractCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Minus(amt) if !newCoins.IsNotNegative() { @@ -157,6 +148,7 @@ func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt // AddCoins adds amt to the coins at the addr. func addCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { + ctx.GasMeter().ConsumeGas(10, "addCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Plus(amt) if !newCoins.IsNotNegative() { diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index a711a893c4fa..38c9ad5760b3 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -42,79 +42,58 @@ func TestKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := coinKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) // Test AddCoins coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 25}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) // Test SubtractCoins coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) - _, err = coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) + _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) assert.Implements(t, (*sdk.Error)(nil), err) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 15}})) - bar, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}}) - assert.False(t, bar) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}})) // Test SendCoins coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -126,40 +105,12 @@ func TestKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } coinKeeper.InputOutputCoins(ctx, inputs, outputs) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - coins, err = coinKeeper.GetCoins(ctx, addr3) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } -func TestKeeperGas(t *testing.T) { - ms, authKey := setupMultiStore() - - cdc := wire.NewCodec() - auth.RegisterBaseAccount(cdc) - - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10) - accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) - coinKeeper := NewKeeper(accountMapper) - - addr := sdk.Address([]byte("addr1")) - acc := accountMapper.NewAccountWithAddress(ctx, addr) - - // Test GetCoins/SetCoins - accountMapper.SetAccount(ctx, acc) - coins, err := coinKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) - - coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.NotNil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) -} - func TestSendKeeper(t *testing.T) { ms, authKey := setupMultiStore() @@ -178,55 +129,40 @@ func TestSendKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := sendKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) // Test SendCoins sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) sendKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -238,12 +174,9 @@ func TestSendKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } sendKeeper.InputOutputCoins(ctx, inputs, outputs) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - coins, err = sendKeeper.GetCoins(ctx, addr3) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -253,7 +186,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) @@ -263,21 +196,14 @@ func TestViewKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := viewKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = viewKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) } From 09517056b07dbd1ba55c7ebffe9b537b11a5907d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:46:02 +0200 Subject: [PATCH 08/32] Catch out-of-gas panics --- baseapp/baseapp.go | 14 ++++++++++---- baseapp/baseapp_test.go | 10 +++++----- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/kvstore/main.go | 2 +- mock/app.go | 2 +- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index aeb534d43ce3..e1198c3c8a0a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -58,7 +58,7 @@ var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp // NOTE: The db is used to store the version number for now. -func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp { +func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGasLimit sdk.Gas) *BaseApp { app := &BaseApp{ Logger: logger, name: name, @@ -67,7 +67,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), - txGasLimit: sdk.Gas(10000), + txGasLimit: txGasLimit, } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -375,8 +375,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // Handle any panics. defer func() { if r := recover(); r != nil { - log := fmt.Sprintf("Recovered: %v\nstack:\n%v", r, string(debug.Stack())) - result = sdk.ErrInternal(log).Result() + switch r.(type) { + case sdk.ErrorOutOfGas: + log := fmt.Sprintf("Out of gas in location: %v", r.(sdk.ErrorOutOfGas).Descriptor) + result = sdk.ErrOutOfGas(log).Result() + default: + log := fmt.Sprintf("Recovered: %v\nstack:\n%v", r, string(debug.Stack())) + result = sdk.ErrInternal(log).Result() + } } }() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index aab64fc20084..3c07e4ac010e 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -25,7 +25,7 @@ func defaultLogger() log.Logger { func newBaseApp(name string) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() - return NewBaseApp(name, nil, logger, db) + return NewBaseApp(name, nil, logger, db, 10000) } func TestMountStores(t *testing.T) { @@ -59,7 +59,7 @@ func TestLoadVersion(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() name := t.Name() - app := NewBaseApp(name, nil, logger, db) + app := NewBaseApp(name, nil, logger, db, 10000) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -81,7 +81,7 @@ func TestLoadVersion(t *testing.T) { commitID := sdk.CommitID{1, res.Data} // reload - app = NewBaseApp(name, nil, logger, db) + app = NewBaseApp(name, nil, logger, db, 10000) app.MountStoresIAVL(capKey) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -147,7 +147,7 @@ func TestInitChainer(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := NewBaseApp(name, nil, logger, db) + app := NewBaseApp(name, nil, logger, db, 10000) // make cap keys and mount the stores // NOTE/TODO: mounting multiple stores is broken // see https://github.com/cosmos/cosmos-sdk/issues/532 @@ -184,7 +184,7 @@ func TestInitChainer(t *testing.T) { assert.Equal(t, value, res.Value) // reload app - app = NewBaseApp(name, nil, logger, db) + app = NewBaseApp(name, nil, logger, db, 10000) app.MountStoresIAVL(capKey, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 5ff532bffab2..672fd5b76c50 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index b1a434fa2c2a..d5994f55bd48 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 7c8250b18938..bf48348b44e3 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index 856538f63aba..bd10d31e94c9 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -32,7 +32,7 @@ func main() { var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("kvstore", nil, logger, db) + var baseApp = bam.NewBaseApp("kvstore", nil, logger, db, 10000) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/mock/app.go b/mock/app.go index ab1a8447a544..4799b726a593 100644 --- a/mock/app.go +++ b/mock/app.go @@ -29,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", nil, logger, db) + baseApp := bam.NewBaseApp("kvstore", nil, logger, db, 10000) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) From ca4ef9a2fca6a5a6175c86820ecb7ef3a2aedc08 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:54:00 +0200 Subject: [PATCH 09/32] Add baseapp test for out-of-gas handling --- baseapp/baseapp_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3c07e4ac010e..913ae1aaacfe 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -260,6 +260,34 @@ func TestDeliverTx(t *testing.T) { } } +// Test that transactions exceeding gas limits fail +func TestTxGasLimits(t *testing.T) { + logger := defaultLogger() + db := dbm.NewMemDB() + app := NewBaseApp(t.Name(), nil, logger, db, 0) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) + app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx.GasMeter().ConsumeGas(10, "counter") + return sdk.Result{} + }) + + tx := testUpdatePowerTx{} // doesn't matter + header := abci.Header{AppHash: []byte("apphash")} + + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Deliver(tx) + assert.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), "Expected transaction to run out of gas") + app.EndBlock(abci.RequestEndBlock{}) + app.Commit() +} + // Test that we can only query from the latest committed state. func TestQuery(t *testing.T) { app := newBaseApp(t.Name()) From c410ceb1552e5dba3a68f3e47acfe86e62a4f2b8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 22:03:42 +0200 Subject: [PATCH 10/32] GasKVStore WIP --- store/gaskvstore.go | 80 ++++++++++++++++++++++++++++++++++++++++ store/gaskvstore_test.go | 27 ++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 store/gaskvstore.go create mode 100644 store/gaskvstore_test.go diff --git a/store/gaskvstore.go b/store/gaskvstore.go new file mode 100644 index 000000000000..b28bfdba9691 --- /dev/null +++ b/store/gaskvstore.go @@ -0,0 +1,80 @@ +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// gasKVStore applies gas tracking to an underlying kvstore +type gasKVStore struct { + gasMeter sdk.GasMeter + parent KVStore +} + +// nolint +func NewGasKVStore(gasMeter sdk.GasMeter, parent KVStore) *gasKVStore { + kvs := &gasKVStore{ + gasMeter: gasMeter, + parent: parent, + } + return kvs +} + +// Implements Store. +func (gi *gasKVStore) GetStoreType() StoreType { + return gi.parent.GetStoreType() +} + +// Implements KVStore. +func (gi *gasKVStore) Get(key []byte) (value []byte) { + return gi.parent.Get(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Set(key []byte, value []byte) { + gi.parent.Set(key, value) +} + +// Implements KVStore. +func (gi *gasKVStore) Has(key []byte) bool { + return gi.parent.Has(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Delete(key []byte) { + gi.parent.Delete(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Iterator(start, end []byte) Iterator { + return gi.iterator(start, end, true) +} + +// Implements KVStore. +func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { + return gi.iterator(start, end, false) +} + +// Implements KVStore. +func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) +} + +// Implements KVStore. +func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) +} + +// Implements KVStore. +func (gi *gasKVStore) CacheWrap() CacheWrap { + return gi.parent.CacheWrap() // TODO +} + +func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { + var parent Iterator + if ascending { + parent = gi.parent.Iterator(start, end) + } else { + parent = gi.parent.ReverseIterator(start, end) + } + return parent // TODO +} diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go new file mode 100644 index 000000000000..dab32dfa1e3c --- /dev/null +++ b/store/gaskvstore_test.go @@ -0,0 +1,27 @@ +package store + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tmlibs/db" +) + +func newGasKVStore() KVStore { + meter := sdk.NewGasMeter(1000) + mem := dbStoreAdapter{dbm.NewMemDB()} + return NewGasKVStore(meter, mem) +} + +func TestGasKVStore(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} From ef1923f6605f797f1c85d668b79da135bef6c06e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 00:27:41 +0200 Subject: [PATCH 11/32] Add GasIterator --- store/gaskvstore.go | 75 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/store/gaskvstore.go b/store/gaskvstore.go index b28bfdba9691..84faab66e8ad 100644 --- a/store/gaskvstore.go +++ b/store/gaskvstore.go @@ -4,6 +4,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +const ( + HasCost = 10 + ReadCostFlat = 10 + ReadCostPerByte = 1 + WriteCostFlat = 10 + WriteCostPerByte = 10 +) + // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { gasMeter sdk.GasMeter @@ -26,21 +34,30 @@ func (gi *gasKVStore) GetStoreType() StoreType { // Implements KVStore. func (gi *gasKVStore) Get(key []byte) (value []byte) { - return gi.parent.Get(key) + gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat") + value = gi.parent.Get(key) + // TODO overflow-safe math? + gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte") + return value } // Implements KVStore. func (gi *gasKVStore) Set(key []byte, value []byte) { + gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat") + // TODO overflow-safe math? + gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } // Implements KVStore. func (gi *gasKVStore) Has(key []byte) bool { + gi.gasMeter.ConsumeGas(HasCost, "Has") return gi.parent.Has(key) } // Implements KVStore. func (gi *gasKVStore) Delete(key []byte) { + // No gas costs for deletion gi.parent.Delete(key) } @@ -66,7 +83,7 @@ func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { // Implements KVStore. func (gi *gasKVStore) CacheWrap() CacheWrap { - return gi.parent.CacheWrap() // TODO + panic("you cannot CacheWrap a GasKVStore") } func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { @@ -76,5 +93,57 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } else { parent = gi.parent.ReverseIterator(start, end) } - return parent // TODO + return newGasIterator(gi.gasMeter, parent) +} + +type gasIterator struct { + gasMeter sdk.GasMeter + parent Iterator +} + +func newGasIterator(gasMeter sdk.GasMeter, parent Iterator) Iterator { + return &gasIterator{ + gasMeter: gasMeter, + parent: parent, + } +} + +// Implements Iterator. +func (g *gasIterator) Domain() (start []byte, end []byte) { + return g.parent.Domain() +} + +// Implements Iterator. +func (g *gasIterator) Valid() bool { + return g.parent.Valid() +} + +/* + TODO + + Not quite sure what to charge for here. Depends on underlying retrieval model. + Could charge for Next(), and Key()/Value() are free, but want to have value-size-proportional gas. +*/ + +// Implements Iterator. +func (g *gasIterator) Next() { + g.parent.Next() +} + +// Implements Iterator. +func (g *gasIterator) Key() (key []byte) { + return g.parent.Key() +} + +// Implements Iterator. +func (g *gasIterator) Value() (value []byte) { + value = g.parent.Value() + g.gasMeter.ConsumeGas(ReadCostFlat, "ValueFlat") + g.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + return value +} + +// Implements Iterator. +func (g *gasIterator) Close() { + g.parent.Close() } From 9dfccb1cfd6b6d4c96a69eb797dda23f60a06ffa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 01:11:22 +0200 Subject: [PATCH 12/32] Update iterator gas pricing model --- store/gaskvstore.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/store/gaskvstore.go b/store/gaskvstore.go index 84faab66e8ad..1bc014229bce 100644 --- a/store/gaskvstore.go +++ b/store/gaskvstore.go @@ -4,12 +4,16 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// nolint const ( HasCost = 10 ReadCostFlat = 10 ReadCostPerByte = 1 WriteCostFlat = 10 WriteCostPerByte = 10 + KeyCostFlat = 5 + ValueCostFlat = 10 + ValueCostPerByte = 1 ) // gasKVStore applies gas tracking to an underlying kvstore @@ -118,13 +122,6 @@ func (g *gasIterator) Valid() bool { return g.parent.Valid() } -/* - TODO - - Not quite sure what to charge for here. Depends on underlying retrieval model. - Could charge for Next(), and Key()/Value() are free, but want to have value-size-proportional gas. -*/ - // Implements Iterator. func (g *gasIterator) Next() { g.parent.Next() @@ -132,14 +129,16 @@ func (g *gasIterator) Next() { // Implements Iterator. func (g *gasIterator) Key() (key []byte) { - return g.parent.Key() + g.gasMeter.ConsumeGas(KeyCostFlat, "KeyFlat") + key = g.parent.Key() + return key } // Implements Iterator. func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() - g.gasMeter.ConsumeGas(ReadCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") + g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") return value } From 1c4ed7b833354a21fc36ebe91980b6558eb113fa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 01:38:32 +0200 Subject: [PATCH 13/32] Gas-wrap ctx.KVStore --- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- .../democoin/x/simplestake/keeper_test.go | 4 +- store/gaskvstore_test.go | 27 -------- types/context.go | 2 +- types/context_test.go | 4 +- {store => types}/gaskvstore.go | 24 +++---- types/gaskvstore_test.go | 63 +++++++++++++++++++ types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +-- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/stake/test_common.go | 2 +- 16 files changed, 92 insertions(+), 60 deletions(-) delete mode 100644 store/gaskvstore_test.go rename {store => types}/gaskvstore.go (81%) create mode 100644 types/gaskvstore_test.go diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 672fd5b76c50..eac94bf5e595 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index d5994f55bd48..06c493d916bf 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index bf48348b44e3..7f6c5fd4d5bb 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index 10e958ce7508..c0b9f3e0ade3 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 100000) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 324cdcdceb9b..17a40d0da26d 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 1d19a61d7894..8f790344f0ab 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go deleted file mode 100644 index dab32dfa1e3c..000000000000 --- a/store/gaskvstore_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package store - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - dbm "github.com/tendermint/tmlibs/db" -) - -func newGasKVStore() KVStore { - meter := sdk.NewGasMeter(1000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, mem) -} - -func TestGasKVStore(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(1000) - st := NewGasKVStore(meter, mem) - - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - - mem.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) -} diff --git a/types/context.go b/types/context.go index 33713d3533e7..91c14373c2b3 100644 --- a/types/context.go +++ b/types/context.go @@ -69,7 +69,7 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return c.multiStore().GetKVStore(key) + return NewGasKVStore(c.GasMeter(), c.multiStore().GetKVStore(key)) } //---------------------------------------- diff --git a/types/context_test.go b/types/context_test.go index 9eafed6259dd..6b1ea1f48dcf 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) return ctx } diff --git a/store/gaskvstore.go b/types/gaskvstore.go similarity index 81% rename from store/gaskvstore.go rename to types/gaskvstore.go index 1bc014229bce..ed8fc0ea9a37 100644 --- a/store/gaskvstore.go +++ b/types/gaskvstore.go @@ -1,8 +1,4 @@ -package store - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) +package types // nolint const ( @@ -18,12 +14,12 @@ const ( // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { - gasMeter sdk.GasMeter + gasMeter GasMeter parent KVStore } // nolint -func NewGasKVStore(gasMeter sdk.GasMeter, parent KVStore) *gasKVStore { +func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { kvs := &gasKVStore{ gasMeter: gasMeter, parent: parent, @@ -41,7 +37,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat") value = gi.parent.Get(key) // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte") + gi.gasMeter.ConsumeGas(ReadCostPerByte*Gas(len(value)), "ReadPerByte") return value } @@ -49,7 +45,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { func (gi *gasKVStore) Set(key []byte, value []byte) { gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat") // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte") + gi.gasMeter.ConsumeGas(WriteCostPerByte*Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } @@ -77,12 +73,12 @@ func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { // Implements KVStore. func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) + return gi.iterator(prefix, PrefixEndBytes(prefix), true) } // Implements KVStore. func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) + return gi.iterator(prefix, PrefixEndBytes(prefix), false) } // Implements KVStore. @@ -101,11 +97,11 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } type gasIterator struct { - gasMeter sdk.GasMeter + gasMeter GasMeter parent Iterator } -func newGasIterator(gasMeter sdk.GasMeter, parent Iterator) Iterator { +func newGasIterator(gasMeter GasMeter, parent Iterator) Iterator { return &gasIterator{ gasMeter: gasMeter, parent: parent, @@ -138,7 +134,7 @@ func (g *gasIterator) Key() (key []byte) { func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostPerByte*Gas(len(value)), "ValuePerByte") return value } diff --git a/types/gaskvstore_test.go b/types/gaskvstore_test.go new file mode 100644 index 000000000000..4ab9c15a1535 --- /dev/null +++ b/types/gaskvstore_test.go @@ -0,0 +1,63 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" +) + +func newGasKVStore() KVStore { + meter := NewGasMeter(1000) + mem := dbStoreAdapter{dbm.NewMemDB()} + return NewGasKVStore(meter, mem) +} + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} + +func TestGasKVStoreOutOfGas(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := NewGasMeter(0) + st := NewGasKVStore(meter, mem) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} + +func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } +func bz(s string) []byte { return []byte(s) } + +type dbStoreAdapter struct { + dbm.DB +} + +// Implements Store. +func (dbStoreAdapter) GetStoreType() StoreType { + return StoreTypeDB +} + +// Implements KVStore. +func (dsa dbStoreAdapter) CacheWrap() CacheWrap { + panic("unsupported") +} + +func (dsa dbStoreAdapter) SubspaceIterator(prefix []byte) Iterator { + return dsa.Iterator(prefix, PrefixEndBytes(prefix)) +} + +func (dsa dbStoreAdapter) ReverseSubspaceIterator(prefix []byte) Iterator { + return dsa.ReverseIterator(prefix, PrefixEndBytes(prefix)) +} + +// dbm.DB implements KVStore so we can CacheKVStore it. +var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index ab6fb605a16a..c29b55a934eb 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 8310d30699bd..2307a35617f7 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index 996a06889865..b7a6003cb03f 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index 80f673f996ad..a4b24f1ab689 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000000) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 5c1c3872322c..29a91d499c67 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 100000000) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From da5fe2ef130c488563842ce189f62b618c4b08d0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 17:54:04 +0200 Subject: [PATCH 14/32] Add baseapp.CheckFull --- baseapp/baseapp.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e1198c3c8a0a..32ee0eec6db2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -314,7 +314,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { if err != nil { result = err.Result() } else { - result = app.runTx(true, txBytes, tx) + result = app.runTx(true, false, txBytes, tx) } return abci.ResponseCheckTx{ @@ -339,7 +339,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if err != nil { result = err.Result() } else { - result = app.runTx(false, txBytes, tx) + result = app.runTx(false, false, txBytes, tx) } // After-handler hooks. @@ -361,17 +361,24 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { } } -// nolint- Mostly for testing +// nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { - return app.runTx(true, nil, tx) + return app.runTx(true, false, nil, tx) } + +// nolint - full tx execution +func (app *BaseApp) CheckFull(tx sdk.Tx) (result sdk.Result) { + return app.runTx(true, true, nil, tx) +} + +// nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(false, nil, tx) + return app.runTx(false, false, nil, tx) } // txBytes may be nil in some cases, eg. in tests. // Also, in the future we may support "internal" transactions. -func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -407,6 +414,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.deliverState.ctx.WithTxBytes(txBytes) } + // Create a new zeroed gas meter + ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) + + // Simulate a DeliverTx for gas calculation + if isCheckTx && fullRun { + ctx = ctx.WithIsCheckTx(false) + } + // Run the ante handler. if app.anteHandler != nil { newCtx, result, abort := app.anteHandler(ctx, tx) @@ -427,7 +442,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // Get the correct cache var msCache sdk.CacheMultiStore - if isCheckTx == true { + if isCheckTx { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -435,7 +450,6 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // CacheWrap app.deliverState.ms in case it fails. msCache = app.deliverState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) - } result = handler(ctx, msg) From 097646e6dfac43dc1fc4f638d75993257f4955cb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 21:57:30 +0200 Subject: [PATCH 15/32] Correct semantics for simulateDeliver --- baseapp/baseapp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 32ee0eec6db2..a6e2dfdf2a94 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -378,7 +378,7 @@ func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { // txBytes may be nil in some cases, eg. in tests. // Also, in the future we may support "internal" transactions. -func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -418,7 +418,7 @@ func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.T ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) // Simulate a DeliverTx for gas calculation - if isCheckTx && fullRun { + if isCheckTx && simulateDeliver { ctx = ctx.WithIsCheckTx(false) } @@ -457,8 +457,8 @@ func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.T // Set gas utilized result.GasUsed = ctx.GasMeter().GasConsumed() - // If result was successful, write to app.checkState.ms or app.deliverState.ms - if result.IsOK() { + // If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms + if !simulateDeliver && result.IsOK() { msCache.Write() } From a2405546959c342df5648b9533f3ebbeb1f01f07 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 22:25:13 +0200 Subject: [PATCH 16/32] SimulateTx through Query --- baseapp/baseapp.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a6e2dfdf2a94..01b6bf272d72 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -3,6 +3,7 @@ package baseapp import ( "fmt" "runtime/debug" + "strings" "github.com/pkg/errors" @@ -27,6 +28,7 @@ type BaseApp struct { // initialized on creation Logger log.Logger name string // application name from abci.Info + codec *wire.Codec // Amino codec db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message @@ -62,6 +64,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa app := &BaseApp{ Logger: logger, name: name, + codec: cdc, db: db, cms: store.NewCommitMultiStore(db), router: NewRouter(), @@ -287,6 +290,28 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { msg := "application doesn't support queries" return sdk.ErrUnknownRequest(msg).QueryResult() } + // Special prefix backslash for special queries + path := req.Path + if strings.HasPrefix(path, "\\") { + query := path[1:] + var result sdk.Result + switch query { + case "simulate": + txBytes := req.Data + tx, err := app.txDecoder(txBytes) + if err != nil { + result = err.Result() + } + result = app.runTx(true, true, txBytes, tx) + default: + result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() + } + value := app.codec.MustMarshalBinary(result) + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: value, + } + } return queryable.Query(req) } From 8c1c40b89a2ac5431e51948ec0b0652f6d73920a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 10 May 2018 20:20:34 +0200 Subject: [PATCH 17/32] New store query prefixes (ref #979) --- baseapp/baseapp.go | 24 +++++++++++++++--------- baseapp/baseapp_test.go | 4 ++-- client/context/helpers.go | 3 +-- mock/app_test.go | 5 ++--- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 01b6bf272d72..680635b49056 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -285,15 +285,10 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // Implements ABCI. // Delegates to CommitMultiStore if it implements Queryable func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - queryable, ok := app.cms.(sdk.Queryable) - if !ok { - msg := "application doesn't support queries" - return sdk.ErrUnknownRequest(msg).QueryResult() - } - // Special prefix backslash for special queries path := req.Path - if strings.HasPrefix(path, "\\") { - query := path[1:] + // "/app" prefix for special application queries + if strings.HasPrefix(path, "/app") { + query := path[4:] var result sdk.Result switch query { case "simulate": @@ -312,7 +307,18 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { Value: value, } } - return queryable.Query(req) + // "/store" prefix for store queries + if strings.HasPrefix(path, "/store") { + queryable, ok := app.cms.(sdk.Queryable) + if !ok { + msg := "multistore doesn't support queries" + return sdk.ErrUnknownRequest(msg).QueryResult() + } + req.Path = req.Path[6:] // slice off "/store" + return queryable.Query(req) + } + msg := "unknown query path" + return sdk.ErrUnknownRequest(msg).QueryResult() } // Implements ABCI diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 913ae1aaacfe..dbfd9f5a8e9f 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -167,7 +167,7 @@ func TestInitChainer(t *testing.T) { } query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: key, } @@ -308,7 +308,7 @@ func TestQuery(t *testing.T) { }) query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: key, } diff --git a/client/context/helpers.go b/client/context/helpers.go index c3dc0a4abb24..47c94c98e902 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -58,8 +58,7 @@ func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName // Query from Tendermint with the provided storename and path func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) { - - path := fmt.Sprintf("/%s/%s", storeName, endPath) + path := fmt.Sprintf("/store/%s/key", storeName) node, err := ctx.GetNode() if err != nil { return res, err diff --git a/mock/app_test.go b/mock/app_test.go index 7c84f9a1d197..be1d778295bb 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -31,10 +31,9 @@ func TestInitApp(t *testing.T) { app.InitChain(req) app.Commit() - // XXX test failing // make sure we can query these values query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: []byte("foo"), } qres := app.Query(query) @@ -70,7 +69,7 @@ func TestDeliverTx(t *testing.T) { // make sure we can query these values query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: []byte(key), } qres := app.Query(query) From 214720318fed7d108bda108137eca3628b901706 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 10 May 2018 20:34:18 +0200 Subject: [PATCH 18/32] Update changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bd85670090..4420a17d6800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ IMPROVEMENTS auto-unbonding * [spec/governance] Fixup some names and pseudocode * NOTE: specs are still a work-in-progress ... +* Gas consumption is now measured as transactions are executed + * Transactions which run out of gas stop execution and revert state changes + * A "simulate" query has been added to determine how much gas a transaction will need + * Modules can include their own gas costs for execution of particular message types +* Bank module now tags transactions with sender/recipient for indexing & later retrieval +* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy BUG FIXES @@ -43,6 +49,7 @@ BREAKING CHANGES * gaiad init now requires use of `--name` flag * Removed Get from Msg interface * types/rational now extends big.Rat +* Queries against the store must be prefixed with the path "/store" FEATURES: @@ -55,7 +62,8 @@ FEATURES: * New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag) * Initialize with genesis txs using `--gen-txs` flag * Context now has access to the application-configured logger - +* Add (non-proof) subspace query helper functions +* Add more staking query functions: candidates, delegator-bonds BUG FIXES * Gaia now uses stake, ported from github.com/cosmos/gaia From 702ffafa061baebc62237012a77881d5e8a716e3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 17:34:24 +0200 Subject: [PATCH 19/32] Rebase --- x/bank/keeper_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 38c9ad5760b3..c65f9528b779 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -65,8 +65,7 @@ func TestKeeper(t *testing.T) { coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) - _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) - assert.Implements(t, (*sdk.Error)(nil), err) + coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) From 147cf9f8975ce9436388944bf2ff8918829b6ca4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 17:46:50 +0200 Subject: [PATCH 20/32] Move GasKVStore to /store --- mock/store.go | 4 +++ store/cachemultistore.go | 5 +++ {types => store}/gaskvstore.go | 44 +++++++++++++----------- store/gaskvstore_test.go | 34 ++++++++++++++++++ store/rootmultistore.go | 5 +++ types/context.go | 2 +- types/gaskvstore_test.go | 63 ---------------------------------- types/store.go | 1 + 8 files changed, 74 insertions(+), 84 deletions(-) rename {types => store}/gaskvstore.go (68%) create mode 100644 store/gaskvstore_test.go delete mode 100644 types/gaskvstore_test.go diff --git a/mock/store.go b/mock/store.go index 329eb250bbde..7f62234eaffa 100644 --- a/mock/store.go +++ b/mock/store.go @@ -50,6 +50,10 @@ func (ms multiStore) GetKVStore(key sdk.StoreKey) sdk.KVStore { return ms.kv[key] } +func (ms multiStore) GetKVStoreWithGas(meter sdk.GasMeter, key sdk.StoreKey) sdk.KVStore { + panic("not implemented") +} + func (ms multiStore) GetStore(key sdk.StoreKey) sdk.Store { panic("not implemented") } diff --git a/store/cachemultistore.go b/store/cachemultistore.go index b1a7548811ea..47878fb15511 100644 --- a/store/cachemultistore.go +++ b/store/cachemultistore.go @@ -72,3 +72,8 @@ func (cms cacheMultiStore) GetStore(key StoreKey) Store { func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore { return cms.stores[key].(KVStore) } + +// Implements MultiStore. +func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore { + return NewGasKVStore(meter, cms.GetKVStore(key)) +} diff --git a/types/gaskvstore.go b/store/gaskvstore.go similarity index 68% rename from types/gaskvstore.go rename to store/gaskvstore.go index ed8fc0ea9a37..9f50f3444162 100644 --- a/types/gaskvstore.go +++ b/store/gaskvstore.go @@ -1,4 +1,8 @@ -package types +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // nolint const ( @@ -14,12 +18,12 @@ const ( // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { - gasMeter GasMeter - parent KVStore + gasMeter sdk.GasMeter + parent sdk.KVStore } // nolint -func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { +func NewGasKVStore(gasMeter sdk.GasMeter, parent sdk.KVStore) *gasKVStore { kvs := &gasKVStore{ gasMeter: gasMeter, parent: parent, @@ -28,7 +32,7 @@ func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { } // Implements Store. -func (gi *gasKVStore) GetStoreType() StoreType { +func (gi *gasKVStore) GetStoreType() sdk.StoreType { return gi.parent.GetStoreType() } @@ -37,7 +41,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat") value = gi.parent.Get(key) // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(ReadCostPerByte*Gas(len(value)), "ReadPerByte") + gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte") return value } @@ -45,7 +49,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { func (gi *gasKVStore) Set(key []byte, value []byte) { gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat") // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(WriteCostPerByte*Gas(len(value)), "SetPerByte") + gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } @@ -62,32 +66,32 @@ func (gi *gasKVStore) Delete(key []byte) { } // Implements KVStore. -func (gi *gasKVStore) Iterator(start, end []byte) Iterator { +func (gi *gasKVStore) Iterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, true) } // Implements KVStore. -func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { +func (gi *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, false) } // Implements KVStore. -func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), true) +func (gi *gasKVStore) SubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) } // Implements KVStore. -func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), false) +func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) } // Implements KVStore. -func (gi *gasKVStore) CacheWrap() CacheWrap { +func (gi *gasKVStore) CacheWrap() sdk.CacheWrap { panic("you cannot CacheWrap a GasKVStore") } -func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { - var parent Iterator +func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { + var parent sdk.Iterator if ascending { parent = gi.parent.Iterator(start, end) } else { @@ -97,11 +101,11 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } type gasIterator struct { - gasMeter GasMeter - parent Iterator + gasMeter sdk.GasMeter + parent sdk.Iterator } -func newGasIterator(gasMeter GasMeter, parent Iterator) Iterator { +func newGasIterator(gasMeter sdk.GasMeter, parent sdk.Iterator) sdk.Iterator { return &gasIterator{ gasMeter: gasMeter, parent: parent, @@ -134,7 +138,7 @@ func (g *gasIterator) Key() (key []byte) { func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ValueCostPerByte*Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") return value } diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go new file mode 100644 index 000000000000..4879c327c2e9 --- /dev/null +++ b/store/gaskvstore_test.go @@ -0,0 +1,34 @@ +package store + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tmlibs/db" +) + +func newGasKVStore() KVStore { + meter := sdk.NewGasMeter(1000) + mem := dbStoreAdapter{dbm.NewMemDB()} + return NewGasKVStore(meter, mem) +} + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} + +func TestGasKVStoreOutOfGas(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(0) + st := NewGasKVStore(meter, mem) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 217e8eb1405e..11cebc22ea9d 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -183,6 +183,11 @@ func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { return rs.stores[key].(KVStore) } +// Implements MultiStore. +func (rs *rootMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore { + return NewGasKVStore(meter, rs.GetKVStore(key)) +} + // getStoreByName will first convert the original name to // a special key, before looking up the CommitStore. // This is not exposed to the extensions (which will need the diff --git a/types/context.go b/types/context.go index 91c14373c2b3..50619f9b7e36 100644 --- a/types/context.go +++ b/types/context.go @@ -69,7 +69,7 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return NewGasKVStore(c.GasMeter(), c.multiStore().GetKVStore(key)) + return c.multiStore().GetKVStoreWithGas(c.GasMeter(), key) } //---------------------------------------- diff --git a/types/gaskvstore_test.go b/types/gaskvstore_test.go deleted file mode 100644 index 4ab9c15a1535..000000000000 --- a/types/gaskvstore_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" -) - -func newGasKVStore() KVStore { - meter := NewGasMeter(1000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, mem) -} - -func TestGasKVStoreBasic(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(1000) - st := NewGasKVStore(meter, mem) - - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - - mem.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) -} - -func TestGasKVStoreOutOfGas(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(0) - st := NewGasKVStore(meter, mem) - require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") -} - -func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } -func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } -func bz(s string) []byte { return []byte(s) } - -type dbStoreAdapter struct { - dbm.DB -} - -// Implements Store. -func (dbStoreAdapter) GetStoreType() StoreType { - return StoreTypeDB -} - -// Implements KVStore. -func (dsa dbStoreAdapter) CacheWrap() CacheWrap { - panic("unsupported") -} - -func (dsa dbStoreAdapter) SubspaceIterator(prefix []byte) Iterator { - return dsa.Iterator(prefix, PrefixEndBytes(prefix)) -} - -func (dsa dbStoreAdapter) ReverseSubspaceIterator(prefix []byte) Iterator { - return dsa.ReverseIterator(prefix, PrefixEndBytes(prefix)) -} - -// dbm.DB implements KVStore so we can CacheKVStore it. -var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/types/store.go b/types/store.go index f8367a1260d1..abf02ec071a5 100644 --- a/types/store.go +++ b/types/store.go @@ -49,6 +49,7 @@ type MultiStore interface { //nolint // Convenience for fetching substores. GetStore(StoreKey) Store GetKVStore(StoreKey) KVStore + GetKVStoreWithGas(GasMeter, StoreKey) KVStore } // From MultiStore.CacheMultiStore().... From ce38d8f423cf0af9f7a8332bdf2e0fd72339fad9 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 20:02:05 +0200 Subject: [PATCH 21/32] Minor fix, testcases --- baseapp/baseapp.go | 7 +++-- baseapp/baseapp_test.go | 66 +++++++++++++++++++++++++++++++++++++++- store/gaskvstore_test.go | 42 ++++++++++++++++++++++--- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 680635b49056..50b915dcbb6c 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -291,13 +291,14 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { query := path[4:] var result sdk.Result switch query { - case "simulate": + case "/simulate": txBytes := req.Data tx, err := app.txDecoder(txBytes) if err != nil { result = err.Result() + } else { + result = app.Simulate(tx) } - result = app.runTx(true, true, txBytes, tx) default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } @@ -398,7 +399,7 @@ func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { } // nolint - full tx execution -func (app *BaseApp) CheckFull(tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) { return app.runTx(true, true, nil, tx) } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index dbfd9f5a8e9f..7a5d77ea5916 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" @@ -16,6 +17,7 @@ import ( "github.com/tendermint/tmlibs/log" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" ) func defaultLogger() log.Logger { @@ -25,7 +27,9 @@ func defaultLogger() log.Logger { func newBaseApp(name string) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() - return NewBaseApp(name, nil, logger, db, 10000) + codec := wire.NewCodec() + wire.RegisterCrypto(codec) + return NewBaseApp(name, codec, logger, db, 10000) } func TestMountStores(t *testing.T) { @@ -260,6 +264,66 @@ func TestDeliverTx(t *testing.T) { } } +func TestSimulateTx(t *testing.T) { + app := newBaseApp(t.Name()) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + counter := 0 + app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) + app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx.GasMeter().ConsumeGas(10, "test") + store := ctx.KVStore(capKey) + // ensure store is never written + require.Nil(t, store.Get([]byte("key"))) + store.Set([]byte("key"), []byte("value")) + // check we can see the current header + thisHeader := ctx.BlockHeader() + height := int64(counter) + assert.Equal(t, height, thisHeader.Height) + counter++ + return sdk.Result{} + }) + + tx := testUpdatePowerTx{} // doesn't matter + header := abci.Header{AppHash: []byte("apphash")} + + app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { + var ttx testUpdatePowerTx + fromJSON(txBytes, &ttx) + return ttx, nil + }) + + nBlocks := 3 + for blockN := 0; blockN < nBlocks; blockN++ { + // block1 + header.Height = int64(blockN + 1) + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + result := app.Simulate(tx) + require.Equal(t, result.Code, sdk.ABCICodeOK) + require.Equal(t, result.GasUsed, int64(80)) + counter-- + encoded, err := json.Marshal(tx) + require.Nil(t, err) + query := abci.RequestQuery{ + Path: "/app/simulate", + Data: encoded, + } + queryResult := app.Query(query) + require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK)) + var res sdk.Result + app.codec.MustUnmarshalBinary(queryResult.Value, &res) + require.Equal(t, res.Code, sdk.ABCICodeOK) + require.Equal(t, res.GasUsed, int64(80)) + app.EndBlock(abci.RequestEndBlock{}) + app.Commit() + } +} + // Test that transactions exceeding gas limits fail func TestTxGasLimits(t *testing.T) { logger := defaultLogger() diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go index 4879c327c2e9..524dc53237c3 100644 --- a/store/gaskvstore_test.go +++ b/store/gaskvstore_test.go @@ -18,17 +18,51 @@ func TestGasKVStoreBasic(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(1000) st := NewGasKVStore(meter, mem) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - - mem.Set(keyFmt(1), valFmt(1)) st.Set(keyFmt(1), valFmt(1)) require.Equal(t, valFmt(1), st.Get(keyFmt(1))) + st.Delete(keyFmt(1)) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Equal(t, meter.GasConsumed(), sdk.Gas(183)) } -func TestGasKVStoreOutOfGas(t *testing.T) { +func TestGasKVStoreIterator(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") + st.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(2), valFmt(2)) + iterator := st.Iterator(nil, nil) + ka := iterator.Key() + require.Equal(t, ka, keyFmt(1)) + va := iterator.Value() + require.Equal(t, va, valFmt(1)) + iterator.Next() + kb := iterator.Key() + require.Equal(t, kb, keyFmt(2)) + vb := iterator.Value() + require.Equal(t, vb, valFmt(2)) + iterator.Next() + require.False(t, iterator.Valid()) + require.Panics(t, iterator.Next) + require.Equal(t, meter.GasConsumed(), sdk.Gas(356)) +} + +func TestGasKVStoreOutOfGasSet(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(0) st := NewGasKVStore(meter, mem) require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") } + +func TestGasKVStoreOutOfGasIterator(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(200) + st := NewGasKVStore(meter, mem) + st.Set(keyFmt(1), valFmt(1)) + iterator := st.Iterator(nil, nil) + iterator.Next() + require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") +} From a801874aba1c0cc55fe0f579fff7310341c00249 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 22:06:53 +0200 Subject: [PATCH 22/32] PR comment: codec => cdc --- baseapp/baseapp.go | 6 +++--- baseapp/baseapp_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 50b915dcbb6c..cbe69d1b7c25 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -28,7 +28,7 @@ type BaseApp struct { // initialized on creation Logger log.Logger name string // application name from abci.Info - codec *wire.Codec // Amino codec + cdc *wire.Codec // Amino codec db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message @@ -64,7 +64,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa app := &BaseApp{ Logger: logger, name: name, - codec: cdc, + cdc: cdc, db: db, cms: store.NewCommitMultiStore(db), router: NewRouter(), @@ -302,7 +302,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } - value := app.codec.MustMarshalBinary(result) + value := app.cdc.MustMarshalBinary(result) return abci.ResponseQuery{ Code: uint32(sdk.ABCICodeOK), Value: value, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 7a5d77ea5916..a605935c7906 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -316,7 +316,7 @@ func TestSimulateTx(t *testing.T) { queryResult := app.Query(query) require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK)) var res sdk.Result - app.codec.MustUnmarshalBinary(queryResult.Value, &res) + app.cdc.MustUnmarshalBinary(queryResult.Value, &res) require.Equal(t, res.Code, sdk.ABCICodeOK) require.Equal(t, res.GasUsed, int64(80)) app.EndBlock(abci.RequestEndBlock{}) From 38716d5edc422896610e28f5b8f154058d89300f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 15:02:54 +0200 Subject: [PATCH 23/32] ConsumeGas for pubkey.VerifyBytes --- x/auth/ante.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x/auth/ante.go b/x/auth/ante.go index 4be1c6674dbc..bf70b3e9b643 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -8,6 +8,10 @@ import ( "github.com/spf13/viper" ) +const ( + VerifyCost = 100 +) + // NewAnteHandler returns an AnteHandler that checks // and increments sequence numbers, checks signatures, // and deducts fees from the first signer. @@ -134,6 +138,7 @@ func processSig( } // Check sig. + ctx.GasMeter().ConsumeGas(VerifyCost, "ante verify") if !pubKey.VerifyBytes(signBytes, sig.Signature) { return nil, sdk.ErrUnauthorized("signature verification failed").Result() } From 4cfa99e21b0f8ed37e8d71415fb4339f52fb769e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 15:11:26 +0200 Subject: [PATCH 24/32] Move to new version in changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4420a17d6800..82c29584e4ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ FEATURES * [x/bank] Tx tags with sender/recipient for indexing & later retrieval * [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy * [x/auth] Added ability to change pubkey to auth module +* Gas consumption is now measured as transactions are executed + * Transactions which run out of gas stop execution and revert state changes + * A "simulate" query has been added to determine how much gas a transaction will need + * Modules can include their own gas costs for execution of particular message types +* Bank module now tags transactions with sender/recipient for indexing & later retrieval +* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy IMPROVEMENTS @@ -23,12 +29,6 @@ IMPROVEMENTS auto-unbonding * [spec/governance] Fixup some names and pseudocode * NOTE: specs are still a work-in-progress ... -* Gas consumption is now measured as transactions are executed - * Transactions which run out of gas stop execution and revert state changes - * A "simulate" query has been added to determine how much gas a transaction will need - * Modules can include their own gas costs for execution of particular message types -* Bank module now tags transactions with sender/recipient for indexing & later retrieval -* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy BUG FIXES From d55ba2ca7d95731ce49166fc4115ce52229cbe64 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:00:17 +0200 Subject: [PATCH 25/32] Add p2p filter functions & tests --- baseapp/baseapp.go | 45 ++++++++++++++++++++++++++++++++++++++--- baseapp/baseapp_test.go | 33 ++++++++++++++++++++++++++++++ types/abci.go | 3 +++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cbe69d1b7c25..8de8492005cc 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -40,9 +40,11 @@ type BaseApp struct { txGasLimit sdk.Gas // per-transaction gas limit // may be nil - 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 + 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 + addrPeerFilter sdk.PeerFilter // filter peers by address and port + pubkeyPeerFilter sdk.PeerFilter // filter peers by public key //-------------------- // Volatile @@ -142,6 +144,12 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { app.anteHandler = ah } +func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) { + app.addrPeerFilter = pf +} +func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) { + app.pubkeyPeerFilter = pf +} func (app *BaseApp) Router() Router { return app.router } // load latest application version @@ -282,6 +290,22 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC return } +// Filter peers by address / port +func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { + if app.addrPeerFilter != nil { + return app.addrPeerFilter(info) + } + return abci.ResponseQuery{} +} + +// Filter peers by public key +func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { + if app.pubkeyPeerFilter != nil { + return app.pubkeyPeerFilter(info) + } + return abci.ResponseQuery{} +} + // Implements ABCI. // Delegates to CommitMultiStore if it implements Queryable func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { @@ -318,6 +342,21 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { req.Path = req.Path[6:] // slice off "/store" return queryable.Query(req) } + // "/p2p" prefix for p2p queries + if strings.HasPrefix(path, "/p2p") { + path = path[4:] + if strings.HasPrefix(path, "/filter") { + path = path[7:] + if strings.HasPrefix(path, "/addr") { + path = path[6:] + return app.FilterPeerByAddrPort(path) + } + if strings.HasPrefix(path, "/pubkey") { + path = path[8:] + return app.FilterPeerByPubKey(path) + } + } + } msg := "unknown query path" return sdk.ErrUnknownRequest(msg).QueryResult() } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index a605935c7906..e54993648fda 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -399,6 +399,39 @@ func TestQuery(t *testing.T) { assert.Equal(t, value, res.Value) } +// Test p2p filter queries +func TestP2PQuery(t *testing.T) { + app := newBaseApp(t.Name()) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + app.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery { + require.Equal(t, "1.1.1.1:8000", addrport) + return abci.ResponseQuery{Code: uint32(3)} + }) + + app.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery { + require.Equal(t, "testpubkey", pubkey) + return abci.ResponseQuery{Code: uint32(4)} + }) + + addrQuery := abci.RequestQuery{ + Path: "/p2p/filter/addr/1.1.1.1:8000", + } + res := app.Query(addrQuery) + require.Equal(t, uint32(3), res.Code) + + pubkeyQuery := abci.RequestQuery{ + Path: "/p2p/filter/pubkey/testpubkey", + } + res = app.Query(pubkeyQuery) + require.Equal(t, uint32(4), res.Code) +} + //---------------------- // TODO: clean this up diff --git a/types/abci.go b/types/abci.go index 40651163c436..a46e797ebe74 100644 --- a/types/abci.go +++ b/types/abci.go @@ -10,3 +10,6 @@ type BeginBlocker func(ctx Context, req abci.RequestBeginBlock) abci.ResponseBeg // run code after the transactions in a block and return updates to the validator set type EndBlocker func(ctx Context, req abci.RequestEndBlock) abci.ResponseEndBlock + +// respond to p2p filtering queries from Tendermint +type PeerFilter func(info string) abci.ResponseQuery From 396bf74b9fd45ddb2d8e6b83e514a39e4df879ac Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:00:49 +0200 Subject: [PATCH 26/32] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c29584e4ac..389f61b43658 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ FEATURES * [x/bank] Tx tags with sender/recipient for indexing & later retrieval * [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy * [x/auth] Added ability to change pubkey to auth module +* baseapp now has settable functions for filtering peers by address/port & public key * Gas consumption is now measured as transactions are executed * Transactions which run out of gas stop execution and revert state changes * A "simulate" query has been added to determine how much gas a transaction will need From 4775437426d7a2bb70cb3b593cb9a81ff82d4624 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:23:57 +0200 Subject: [PATCH 27/32] Unexport verifyCost --- x/auth/ante.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/auth/ante.go b/x/auth/ante.go index bf70b3e9b643..69b867e17c6c 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -9,7 +9,7 @@ import ( ) const ( - VerifyCost = 100 + verifyCost = 100 ) // NewAnteHandler returns an AnteHandler that checks @@ -138,7 +138,7 @@ func processSig( } // Check sig. - ctx.GasMeter().ConsumeGas(VerifyCost, "ante verify") + ctx.GasMeter().ConsumeGas(verifyCost, "ante verify") if !pubKey.VerifyBytes(signBytes, sig.Signature) { return nil, sdk.ErrUnauthorized("signature verification failed").Result() } From 0cc1c52077353dcb98b8e061145ce70d233a4fd3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 01:15:49 +0200 Subject: [PATCH 28/32] Rebase changelog --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 389f61b43658..3361c43616de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,13 +13,13 @@ FEATURES * [x/bank] Tx tags with sender/recipient for indexing & later retrieval * [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy * [x/auth] Added ability to change pubkey to auth module -* baseapp now has settable functions for filtering peers by address/port & public key -* Gas consumption is now measured as transactions are executed +* [baseapp] baseapp now has settable functions for filtering peers by address/port & public key +* [sdk] Gas consumption is now measured as transactions are executed * Transactions which run out of gas stop execution and revert state changes * A "simulate" query has been added to determine how much gas a transaction will need * Modules can include their own gas costs for execution of particular message types -* Bank module now tags transactions with sender/recipient for indexing & later retrieval -* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy +* [x/bank] Bank module now tags transactions with sender/recipient for indexing & later retrieval +* [x/stake] Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy IMPROVEMENTS From 4134bf922c6c4c93b3a178a36d818457b44c3d25 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:06:17 +0200 Subject: [PATCH 29/32] Address PR comments --- baseapp/baseapp.go | 68 +++++++++++++++++-------------- baseapp/baseapp_test.go | 6 +-- client/context/helpers.go | 1 + cmd/gaia/app/app_test.go | 2 +- examples/basecoin/app/app_test.go | 2 +- examples/democoin/app/app_test.go | 2 +- x/auth/ante.go | 3 ++ x/bank/keeper.go | 18 +++++--- 8 files changed, 61 insertions(+), 41 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 8de8492005cc..60e32afa4a83 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -23,6 +23,17 @@ import ( // and to avoid affecting the Merkle root. var dbHeaderKey = []byte("header") +type RunTxMode uint8 + +const ( + // Check a transaction + RunTxModeCheck RunTxMode = iota + // Simulate a transaction + RunTxModeSimulate RunTxMode = iota + // Deliver a transaction + RunTxModeDeliver RunTxMode = iota +) + // The ABCI application type BaseApp struct { // initialized on creation @@ -309,13 +320,17 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { // Implements ABCI. // Delegates to CommitMultiStore if it implements Queryable func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - path := req.Path + path := strings.Split(req.Path, "/") + // first element is empty string + if len(path) > 0 && path[0] == "" { + path = path[1:] + } + fmt.Sprintf("Path: %v\n", path) // "/app" prefix for special application queries - if strings.HasPrefix(path, "/app") { - query := path[4:] + if len(path) >= 2 && path[0] == "app" { var result sdk.Result - switch query { - case "/simulate": + switch path[1] { + case "simulate": txBytes := req.Data tx, err := app.txDecoder(txBytes) if err != nil { @@ -333,27 +348,23 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } } // "/store" prefix for store queries - if strings.HasPrefix(path, "/store") { + if len(path) >= 1 && path[0] == "store" { queryable, ok := app.cms.(sdk.Queryable) if !ok { msg := "multistore doesn't support queries" return sdk.ErrUnknownRequest(msg).QueryResult() } - req.Path = req.Path[6:] // slice off "/store" + req.Path = "/" + strings.Join(path[1:], "/") return queryable.Query(req) } // "/p2p" prefix for p2p queries - if strings.HasPrefix(path, "/p2p") { - path = path[4:] - if strings.HasPrefix(path, "/filter") { - path = path[7:] - if strings.HasPrefix(path, "/addr") { - path = path[6:] - return app.FilterPeerByAddrPort(path) + if len(path) >= 4 && path[0] == "p2p" { + if path[1] == "filter" { + if path[2] == "addr" { + return app.FilterPeerByAddrPort(path[3]) } - if strings.HasPrefix(path, "/pubkey") { - path = path[8:] - return app.FilterPeerByPubKey(path) + if path[2] == "pubkey" { + return app.FilterPeerByPubKey(path[3]) } } } @@ -385,7 +396,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { if err != nil { result = err.Result() } else { - result = app.runTx(true, false, txBytes, tx) + result = app.runTx(RunTxModeCheck, txBytes, tx) } return abci.ResponseCheckTx{ @@ -410,7 +421,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if err != nil { result = err.Result() } else { - result = app.runTx(false, false, txBytes, tx) + result = app.runTx(RunTxModeDeliver, txBytes, tx) } // After-handler hooks. @@ -434,22 +445,22 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { - return app.runTx(true, false, nil, tx) + return app.runTx(RunTxModeCheck, nil, tx) } // nolint - full tx execution func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) { - return app.runTx(true, true, nil, tx) + return app.runTx(RunTxModeSimulate, nil, tx) } // nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(false, false, nil, tx) + return app.runTx(RunTxModeDeliver, nil, tx) } // txBytes may be nil in some cases, eg. in tests. // Also, in the future we may support "internal" transactions. -func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -479,17 +490,14 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, // Get the context var ctx sdk.Context - if isCheckTx { + if mode == RunTxModeCheck || mode == RunTxModeSimulate { ctx = app.checkState.ctx.WithTxBytes(txBytes) } else { ctx = app.deliverState.ctx.WithTxBytes(txBytes) } - // Create a new zeroed gas meter - ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) - // Simulate a DeliverTx for gas calculation - if isCheckTx && simulateDeliver { + if mode == RunTxModeSimulate { ctx = ctx.WithIsCheckTx(false) } @@ -513,7 +521,7 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, // Get the correct cache var msCache sdk.CacheMultiStore - if isCheckTx { + if mode == RunTxModeCheck || mode == RunTxModeSimulate { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -529,7 +537,7 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, result.GasUsed = ctx.GasMeter().GasConsumed() // If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms - if !simulateDeliver && result.IsOK() { + if mode != RunTxModeSimulate && result.IsOK() { msCache.Write() } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index e54993648fda..25de7ef7e420 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -305,7 +305,7 @@ func TestSimulateTx(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{Header: header}) result := app.Simulate(tx) require.Equal(t, result.Code, sdk.ABCICodeOK) - require.Equal(t, result.GasUsed, int64(80)) + require.Equal(t, int64(80), result.GasUsed) counter-- encoded, err := json.Marshal(tx) require.Nil(t, err) @@ -317,8 +317,8 @@ func TestSimulateTx(t *testing.T) { require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK)) var res sdk.Result app.cdc.MustUnmarshalBinary(queryResult.Value, &res) - require.Equal(t, res.Code, sdk.ABCICodeOK) - require.Equal(t, res.GasUsed, int64(80)) + require.Equal(t, sdk.ABCICodeOK, res.Code) + require.Equal(t, int64(160), res.GasUsed) app.EndBlock(abci.RequestEndBlock{}) app.Commit() } diff --git a/client/context/helpers.go b/client/context/helpers.go index 47c94c98e902..562bde9b4ca7 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -113,6 +113,7 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w ChainID: chainID, Sequences: []int64{sequence}, Msg: msg, + Fee: sdk.NewStdFee(10000, sdk.Coin{}), // TODO run simulate to estimate gas? } keybase, err := keys.GetKeyBase() diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 3bca2654b839..fd26ce27a319 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -40,7 +40,7 @@ var ( manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 100000, } sendMsg1 = bank.MsgSend{ diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 034e198609ad..d0b59f331372 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -39,7 +39,7 @@ var ( manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 100000, } sendMsg1 = bank.MsgSend{ diff --git a/examples/democoin/app/app_test.go b/examples/democoin/app/app_test.go index c67782d926e3..b0f188f10b37 100644 --- a/examples/democoin/app/app_test.go +++ b/examples/democoin/app/app_test.go @@ -33,7 +33,7 @@ var ( coins = sdk.Coins{{"foocoin", 10}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 1000000, } sendMsg = bank.MsgSend{ diff --git a/x/auth/ante.go b/x/auth/ante.go index 69b867e17c6c..248083206d5c 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -92,6 +92,9 @@ func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHan // cache the signer accounts in the context ctx = WithSigners(ctx, signerAccs) + // set the gas meter + ctx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) + // TODO: tx tags (?) return ctx, sdk.Result{}, false // continue... diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 129ffa79230b..6ef73c68b619 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -6,6 +6,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +const ( + costGetCoins sdk.Gas = 10 + costHasCoins sdk.Gas = 10 + costSetCoins sdk.Gas = 100 + costSubtractCoins sdk.Gas = 10 + costAddCoins sdk.Gas = 10 +) + // Keeper manages transfers between accounts type Keeper struct { am sdk.AccountMapper @@ -108,7 +116,7 @@ func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coi //______________________________________________________________________________________________ func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { - ctx.GasMeter().ConsumeGas(10, "getCoins") + ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins") acc := am.GetAccount(ctx, addr) if acc == nil { return sdk.Coins{} @@ -117,7 +125,7 @@ func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { - ctx.GasMeter().ConsumeGas(100, "setCoins") + ctx.GasMeter().ConsumeGas(costSetCoins, "setCoins") acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) @@ -129,13 +137,13 @@ func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.C // HasCoins returns whether or not an account has at least amt coins. func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool { - ctx.GasMeter().ConsumeGas(10, "hasCoins") + ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins") return getCoins(ctx, am, addr).IsGTE(amt) } // SubtractCoins subtracts amt from the coins at the addr. func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { - ctx.GasMeter().ConsumeGas(10, "subtractCoins") + ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Minus(amt) if !newCoins.IsNotNegative() { @@ -148,7 +156,7 @@ func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt // AddCoins adds amt to the coins at the addr. func addCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { - ctx.GasMeter().ConsumeGas(10, "addCoins") + ctx.GasMeter().ConsumeGas(costAddCoins, "addCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Plus(amt) if !newCoins.IsNotNegative() { From 03e220700e73c1dc7d30d0d49e60bf83523f0e92 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:18:25 +0200 Subject: [PATCH 30/32] Unexport RunTxMode (fix linter) --- baseapp/baseapp.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 60e32afa4a83..223da0ea566c 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -23,15 +23,16 @@ import ( // and to avoid affecting the Merkle root. var dbHeaderKey = []byte("header") -type RunTxMode uint8 +// Enum mode for app.runTx +type runTxMode uint8 const ( // Check a transaction - RunTxModeCheck RunTxMode = iota + runTxModeCheck runTxMode = iota // Simulate a transaction - RunTxModeSimulate RunTxMode = iota + runTxModeSimulate runTxMode = iota // Deliver a transaction - RunTxModeDeliver RunTxMode = iota + runTxModeDeliver runTxMode = iota ) // The ABCI application @@ -396,7 +397,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { if err != nil { result = err.Result() } else { - result = app.runTx(RunTxModeCheck, txBytes, tx) + result = app.runTx(runTxModeCheck, txBytes, tx) } return abci.ResponseCheckTx{ @@ -421,7 +422,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if err != nil { result = err.Result() } else { - result = app.runTx(RunTxModeDeliver, txBytes, tx) + result = app.runTx(runTxModeDeliver, txBytes, tx) } // After-handler hooks. @@ -445,22 +446,22 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { - return app.runTx(RunTxModeCheck, nil, tx) + return app.runTx(runTxModeCheck, nil, tx) } // nolint - full tx execution func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) { - return app.runTx(RunTxModeSimulate, nil, tx) + return app.runTx(runTxModeSimulate, nil, tx) } // nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(RunTxModeDeliver, nil, tx) + return app.runTx(runTxModeDeliver, nil, tx) } // txBytes may be nil in some cases, eg. in tests. // Also, in the future we may support "internal" transactions. -func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -490,14 +491,14 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk // Get the context var ctx sdk.Context - if mode == RunTxModeCheck || mode == RunTxModeSimulate { + if mode == runTxModeCheck || mode == runTxModeSimulate { ctx = app.checkState.ctx.WithTxBytes(txBytes) } else { ctx = app.deliverState.ctx.WithTxBytes(txBytes) } // Simulate a DeliverTx for gas calculation - if mode == RunTxModeSimulate { + if mode == runTxModeSimulate { ctx = ctx.WithIsCheckTx(false) } @@ -521,7 +522,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk // Get the correct cache var msCache sdk.CacheMultiStore - if mode == RunTxModeCheck || mode == RunTxModeSimulate { + if mode == runTxModeCheck || mode == runTxModeSimulate { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -537,7 +538,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk result.GasUsed = ctx.GasMeter().GasConsumed() // If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms - if mode != RunTxModeSimulate && result.IsOK() { + if mode != runTxModeSimulate && result.IsOK() { msCache.Write() } From 3d5b0484441490cabac73f4c0a142f57c7d63fcf Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:31:52 +0200 Subject: [PATCH 31/32] Remove txGasLimit, update tests --- baseapp/baseapp.go | 12 +++++------- baseapp/baseapp_test.go | 17 ++++++++++------- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/kvstore/main.go | 2 +- mock/app.go | 2 +- types/context.go | 2 +- types/gas.go | 18 ++++++++++++++++++ 9 files changed, 39 insertions(+), 20 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 223da0ea566c..1da26fe3f3d9 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -49,7 +49,6 @@ type BaseApp struct { // must be set txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth - txGasLimit sdk.Gas // per-transaction gas limit // may be nil initChainer sdk.InitChainer // initialize state with validators and state blob @@ -74,7 +73,7 @@ var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp // NOTE: The db is used to store the version number for now. -func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGasLimit sdk.Gas) *BaseApp { +func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp { app := &BaseApp{ Logger: logger, name: name, @@ -84,7 +83,6 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), - txGasLimit: txGasLimit, } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -235,9 +233,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, app.txGasLimit) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, 0) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, app.txGasLimit) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, 0) } type state struct { @@ -253,7 +251,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger, app.txGasLimit), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger, 0), } } @@ -261,7 +259,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger, app.txGasLimit), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger, 0), } } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 25de7ef7e420..969a9f9adda6 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -29,7 +29,7 @@ func newBaseApp(name string) *BaseApp { db := dbm.NewMemDB() codec := wire.NewCodec() wire.RegisterCrypto(codec) - return NewBaseApp(name, codec, logger, db, 10000) + return NewBaseApp(name, codec, logger, db) } func TestMountStores(t *testing.T) { @@ -63,7 +63,7 @@ func TestLoadVersion(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() name := t.Name() - app := NewBaseApp(name, nil, logger, db, 10000) + app := NewBaseApp(name, nil, logger, db) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -85,7 +85,7 @@ func TestLoadVersion(t *testing.T) { commitID := sdk.CommitID{1, res.Data} // reload - app = NewBaseApp(name, nil, logger, db, 10000) + app = NewBaseApp(name, nil, logger, db) app.MountStoresIAVL(capKey) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -151,7 +151,7 @@ func TestInitChainer(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := NewBaseApp(name, nil, logger, db, 10000) + app := NewBaseApp(name, nil, logger, db) // make cap keys and mount the stores // NOTE/TODO: mounting multiple stores is broken // see https://github.com/cosmos/cosmos-sdk/issues/532 @@ -188,7 +188,7 @@ func TestInitChainer(t *testing.T) { assert.Equal(t, value, res.Value) // reload app - app = NewBaseApp(name, nil, logger, db, 10000) + app = NewBaseApp(name, nil, logger, db) app.MountStoresIAVL(capKey, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -328,7 +328,7 @@ func TestSimulateTx(t *testing.T) { func TestTxGasLimits(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() - app := NewBaseApp(t.Name(), nil, logger, db, 0) + app := NewBaseApp(t.Name(), nil, logger, db) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -336,7 +336,10 @@ func TestTxGasLimits(t *testing.T) { err := app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) - app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) + app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { + newCtx = ctx.WithGasMeter(sdk.NewGasMeter(0)) + return + }) app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { ctx.GasMeter().ConsumeGas(10, "counter") return sdk.Result{} diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index eac94bf5e595..5ff532bffab2 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 06c493d916bf..b1a434fa2c2a 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 7f6c5fd4d5bb..7c8250b18938 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index bd10d31e94c9..856538f63aba 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -32,7 +32,7 @@ func main() { var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("kvstore", nil, logger, db, 10000) + var baseApp = bam.NewBaseApp("kvstore", nil, logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/mock/app.go b/mock/app.go index 4799b726a593..ab1a8447a544 100644 --- a/mock/app.go +++ b/mock/app.go @@ -29,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", nil, logger, db, 10000) + baseApp := bam.NewBaseApp("kvstore", nil, logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/types/context.go b/types/context.go index 50619f9b7e36..4ffd881606fe 100644 --- a/types/context.go +++ b/types/context.go @@ -43,7 +43,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byt c = c.WithIsCheckTx(isCheckTx) c = c.WithTxBytes(txBytes) c = c.WithLogger(logger) - c = c.WithGasMeter(NewGasMeter(gasLimit)) + c = c.WithGasMeter(NewInfiniteGasMeter()) return c } diff --git a/types/gas.go b/types/gas.go index 78246be2d1d1..49bfa27ece87 100644 --- a/types/gas.go +++ b/types/gas.go @@ -38,3 +38,21 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { panic(ErrorOutOfGas{descriptor}) } } + +type infiniteGasMeter struct { + consumed Gas +} + +func NewInfiniteGasMeter() GasMeter { + return &infiniteGasMeter{ + consumed: 0, + } +} + +func (g *infiniteGasMeter) GasConsumed() Gas { + return g.consumed +} + +func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { + g.consumed += amount +} From 4bdcad572be8de20614ff0606c4ebf7d4727b547 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 May 2018 22:19:09 -0400 Subject: [PATCH 32/32] remove gasLimit from NewContext --- baseapp/baseapp.go | 9 ++++----- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/handler_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- examples/democoin/x/simplestake/keeper_test.go | 4 ++-- types/context.go | 2 +- types/context_test.go | 4 ++-- types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +++++----- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/bank/keeper_test.go | 6 +++--- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 14 files changed, 25 insertions(+), 26 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 1da26fe3f3d9..ef3bbc3c79d2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -233,9 +233,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, 0) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, 0) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger) } type state struct { @@ -251,7 +251,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger, 0), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger), } } @@ -259,7 +259,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger, 0), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger), } } @@ -324,7 +324,6 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { if len(path) > 0 && path[0] == "" { path = path[1:] } - fmt.Sprintf("Path: %v\n", path) // "/app" prefix for special application queries if len(path) >= 2 && path[0] == "app" { var result sdk.Result diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index c0b9f3e0ade3..d497dee6996d 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index df83125025bb..30aeafa2a566 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 17a40d0da26d..a4afb876a94b 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 8f790344f0ab..302a2e58b6b0 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/types/context.go b/types/context.go index 4ffd881606fe..4ab0a5d0932e 100644 --- a/types/context.go +++ b/types/context.go @@ -30,7 +30,7 @@ type Context struct { } // create a new context -func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger, gasLimit Gas) Context { +func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context { c := Context{ Context: context.Background(), pst: newThePast(), diff --git a/types/context_test.go b/types/context_test.go index 6b1ea1f48dcf..ec5faab440f3 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) return ctx } diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index c29b55a934eb..e1759b06ac89 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 2307a35617f7..ec296b12beba 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index b7a6003cb03f..89e318e0a1db 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index a4b24f1ab689..cdd418990a9c 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index c65f9528b779..117c69e7aece 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -116,7 +116,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -185,7 +185,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index e05245ff3878..60cc59bad9f8 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 29a91d499c67..27acebe0862a 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 100000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger()) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec