Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas management, estimation, limitation #963

Merged
merged 32 commits into from
May 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
46f9445
Add gas limit / gas consumed to context
cwgoes May 7, 2018
2699180
GasMeter & context updates
cwgoes May 7, 2018
efc7843
Changes to bank keeper for gas
cwgoes May 7, 2018
f0e4d24
Basic gas impl, quick testcase
cwgoes May 7, 2018
ddb3b36
Pass gas consumed back in result struct
cwgoes May 7, 2018
af379b6
Linter fixes
cwgoes May 7, 2018
1f8ef62
Swap to panic/recover version
cwgoes May 8, 2018
0951705
Catch out-of-gas panics
cwgoes May 8, 2018
ca4ef9a
Add baseapp test for out-of-gas handling
cwgoes May 8, 2018
c410ceb
GasKVStore WIP
cwgoes May 8, 2018
ef1923f
Add GasIterator
cwgoes May 8, 2018
9dfccb1
Update iterator gas pricing model
cwgoes May 8, 2018
1c4ed7b
Gas-wrap ctx.KVStore
cwgoes May 8, 2018
da5fe2e
Add baseapp.CheckFull
cwgoes May 9, 2018
097646e
Correct semantics for simulateDeliver
cwgoes May 9, 2018
a240554
SimulateTx through Query
cwgoes May 9, 2018
8c1c40b
New store query prefixes (ref #979)
cwgoes May 10, 2018
2147203
Update changelog
cwgoes May 10, 2018
702ffaf
Rebase
cwgoes May 11, 2018
147cf9f
Move GasKVStore to /store
cwgoes May 11, 2018
ce38d8f
Minor fix, testcases
cwgoes May 11, 2018
a801874
PR comment: codec => cdc
cwgoes May 11, 2018
38716d5
ConsumeGas for pubkey.VerifyBytes
cwgoes May 15, 2018
4cfa99e
Move to new version in changelog
cwgoes May 15, 2018
d55ba2c
Add p2p filter functions & tests
cwgoes May 15, 2018
396bf74
Update changelog
cwgoes May 15, 2018
4775437
Unexport verifyCost
cwgoes May 15, 2018
0cc1c52
Rebase changelog
cwgoes May 15, 2018
4134bf9
Address PR comments
cwgoes May 16, 2018
03e2207
Unexport RunTxMode (fix linter)
cwgoes May 16, 2018
3d5b048
Remove txGasLimit, update tests
cwgoes May 16, 2018
4bdcad5
remove gasLimit from NewContext
ebuchman May 16, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +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] 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
* [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

Expand Down Expand Up @@ -43,6 +50,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:

Expand All @@ -55,7 +63,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
Expand Down
145 changes: 124 additions & 21 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package baseapp
import (
"fmt"
"runtime/debug"
"strings"

"github.com/pkg/errors"

Expand All @@ -22,11 +23,24 @@ import (
// and to avoid affecting the Merkle root.
var dbHeaderKey = []byte("header")

// Enum mode for app.runTx
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
Logger log.Logger
name string // application name from abci.Info
cdc *wire.Codec // Amino codec
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
Expand All @@ -37,9 +51,11 @@ type BaseApp struct {
anteHandler sdk.AnteHandler // ante handler for fee and auth

// 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
Expand All @@ -61,6 +77,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas
app := &BaseApp{
Logger: logger,
name: name,
cdc: cdc,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
Expand Down Expand Up @@ -137,6 +154,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
Expand Down Expand Up @@ -277,15 +300,74 @@ 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) {
queryable, ok := app.cms.(sdk.Queryable)
if !ok {
msg := "application doesn't support queries"
return sdk.ErrUnknownRequest(msg).QueryResult()
path := strings.Split(req.Path, "/")
// first element is empty string
if len(path) > 0 && path[0] == "" {
path = path[1:]
}
// "/app" prefix for special application queries
if len(path) >= 2 && path[0] == "app" {
var result sdk.Result
switch path[1] {
case "simulate":
txBytes := req.Data
tx, err := app.txDecoder(txBytes)
if err != nil {
result = err.Result()
} else {
result = app.Simulate(tx)
}
default:
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
}
value := app.cdc.MustMarshalBinary(result)
return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK),
Value: value,
}
}
return queryable.Query(req)
// "/store" prefix for store queries
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 = "/" + strings.Join(path[1:], "/")
return queryable.Query(req)
}
// "/p2p" prefix for p2p queries
if len(path) >= 4 && path[0] == "p2p" {
if path[1] == "filter" {
if path[2] == "addr" {
return app.FilterPeerByAddrPort(path[3])
}
if path[2] == "pubkey" {
return app.FilterPeerByPubKey(path[3])
}
}
}
msg := "unknown query path"
return sdk.ErrUnknownRequest(msg).QueryResult()
}

// Implements ABCI
Expand All @@ -312,14 +394,15 @@ 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(runTxModeCheck, txBytes, tx)
}

return abci.ResponseCheckTx{
Code: uint32(result.Code),
Data: result.Data,
Log: result.Log,
GasWanted: result.GasWanted,
GasUsed: result.GasUsed,
Fee: cmn.KI64Pair{
[]byte(result.FeeDenom),
result.FeeAmount,
Expand All @@ -336,7 +419,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(runTxModeDeliver, txBytes, tx)
}

// After-handler hooks.
Expand All @@ -358,22 +441,35 @@ 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(runTxModeCheck, nil, tx)
}

// nolint - full tx execution
func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) {
return app.runTx(runTxModeSimulate, nil, tx)
}

// nolint
func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) {
return app.runTx(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, 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 {
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()
}
}
}()

Expand All @@ -392,12 +488,17 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk

// 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)
}

// Simulate a DeliverTx for gas calculation
if mode == runTxModeSimulate {
ctx = ctx.WithIsCheckTx(false)
}

// Run the ante handler.
if app.anteHandler != nil {
newCtx, result, abort := app.anteHandler(ctx, tx)
Expand All @@ -418,21 +519,23 @@ 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 mode == runTxModeCheck || mode == runTxModeSimulate {
// CacheWrap app.checkState.ms in case it fails.
msCache = app.checkState.CacheMultiStore()
ctx = ctx.WithMultiStore(msCache)
} else {
// CacheWrap app.deliverState.ms in case it fails.
msCache = app.deliverState.CacheMultiStore()
ctx = ctx.WithMultiStore(msCache)

}

result = handler(ctx, msg)

// If result was successful, write to app.checkState.ms or app.deliverState.ms
if result.IsOK() {
// Set gas utilized
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() {
msCache.Write()
}

Expand Down
Loading