diff --git a/baseapp/abci.go b/baseapp/abci.go new file mode 100644 index 000000000000..067732b940bf --- /dev/null +++ b/baseapp/abci.go @@ -0,0 +1,407 @@ +package baseapp + +import ( + "fmt" + "os" + "sort" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// InitChain implements the ABCI interface. It runs the initialization logic +// directly on the CommitMultiStore. +func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { + // stash the consensus params in the cms main store and memoize + if req.ConsensusParams != nil { + app.setConsensusParams(req.ConsensusParams) + app.storeConsensusParams(req.ConsensusParams) + } + + initHeader := abci.Header{ChainID: req.ChainId, Time: req.Time} + + // initialize the deliver state and check state with a correct header + app.setDeliverState(initHeader) + app.setCheckState(initHeader) + + if app.initChainer == nil { + return + } + + // add block gas meter for any genesis transactions (allow infinite gas) + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + + res = app.initChainer(app.deliverState.ctx, req) + + // sanity check + if len(req.Validators) > 0 { + if len(req.Validators) != len(res.Validators) { + panic(fmt.Errorf( + "len(RequestInitChain.Validators) != len(validators) (%d != %d)", + len(req.Validators), len(res.Validators))) + } + sort.Sort(abci.ValidatorUpdates(req.Validators)) + sort.Sort(abci.ValidatorUpdates(res.Validators)) + for i, val := range res.Validators { + if !val.Equal(req.Validators[i]) { + panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i)) + } + } + } + + // NOTE: We don't commit, but BeginBlock for block 1 starts from this + // deliverState. + return +} + +// Info implements the ABCI interface. +func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { + lastCommitID := app.cms.LastCommitID() + + return abci.ResponseInfo{ + Data: app.name, + LastBlockHeight: lastCommitID.Version, + LastBlockAppHash: lastCommitID.Hash, + } +} + +// SetOption implements the ABCI interface. +func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) { + // TODO: Implement! + return +} + +// FilterPeerByAddrPort filters peers by address/port. +func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { + if app.addrPeerFilter != nil { + return app.addrPeerFilter(info) + } + return abci.ResponseQuery{} +} + +// FilterPeerByIDfilters peers by node ID. +func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { + if app.idPeerFilter != nil { + return app.idPeerFilter(info) + } + return abci.ResponseQuery{} +} + +// BeginBlock implements the ABCI application interface. +func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { + if app.cms.TracingEnabled() { + app.cms.SetTracingContext(sdk.TraceContext( + map[string]interface{}{"blockHeight": req.Header.Height}, + )) + } + + if err := app.validateHeight(req); err != nil { + panic(err) + } + + // Initialize the DeliverTx state. If this is the first block, it should + // already be initialized in InitChain. Otherwise app.deliverState will be + // nil, since it is reset on Commit. + if app.deliverState == nil { + app.setDeliverState(req.Header) + } else { + // In the first block, app.deliverState.ctx will already be initialized + // by InitChain. Context is now updated with Header information. + app.deliverState.ctx = app.deliverState.ctx. + WithBlockHeader(req.Header). + WithBlockHeight(req.Header.Height) + } + + // add block gas meter + var gasMeter sdk.GasMeter + if maxGas := app.getMaximumBlockGas(); maxGas > 0 { + gasMeter = sdk.NewGasMeter(maxGas) + } else { + gasMeter = sdk.NewInfiniteGasMeter() + } + + app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter) + + if app.beginBlocker != nil { + res = app.beginBlocker(app.deliverState.ctx, req) + } + + // set the signed validators for addition to context in deliverTx + app.voteInfos = req.LastCommitInfo.GetVotes() + return +} + +// EndBlock implements the ABCI interface. +func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + if app.deliverState.ms.TracingEnabled() { + app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) + } + + if app.endBlocker != nil { + res = app.endBlocker(app.deliverState.ctx, req) + } + + return +} + +// CheckTx implements the ABCI interface. It runs the "basic checks" to see +// whether or not a transaction can possibly be executed, first decoding and then +// the ante handler (which checks signatures/fees/ValidateBasic). +// +// NOTE:CheckTx does not run the actual Msg handler function(s). +func (app *BaseApp) CheckTx(req abci.RequestCheckTx) (res abci.ResponseCheckTx) { + var result sdk.Result + + tx, err := app.txDecoder(req.Tx) + if err != nil { + result = err.Result() + } else { + result = app.runTx(runTxModeCheck, req.Tx, tx) + } + + return abci.ResponseCheckTx{ + Code: uint32(result.Code), + Data: result.Data, + Log: result.Log, + GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? + GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? + Events: result.Events.ToABCIEvents(), + } +} + +// DeliverTx implements the ABCI interface. +func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) { + var result sdk.Result + + tx, err := app.txDecoder(req.Tx) + if err != nil { + result = err.Result() + } else { + result = app.runTx(runTxModeDeliver, req.Tx, tx) + } + + return abci.ResponseDeliverTx{ + Code: uint32(result.Code), + Codespace: string(result.Codespace), + Data: result.Data, + Log: result.Log, + GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? + GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? + Events: result.Events.ToABCIEvents(), + } +} + +// Commit implements the ABCI interface. It will commit all state that exists in +// the deliver state's multi-store and includes the resulting commit ID in the +// returned abci.ResponseCommit. Commit will set the check state based on the +// latest header and reset the deliver state. Also, if a non-zero halt height is +// defined in config, Commit will execute a deferred function call to check +// against that height and gracefully halt if it matches the latest committed +// height. +func (app *BaseApp) Commit() (res abci.ResponseCommit) { + header := app.deliverState.ctx.BlockHeader() + + // write the Deliver state and commit the MultiStore + app.deliverState.ms.Write() + commitID := app.cms.Commit() + app.logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID)) + + // Reset the Check state to the latest committed. + // + // NOTE: This is safe because Tendermint holds a lock on the mempool for + // Commit. Use the header from this latest block. + app.setCheckState(header) + + // empty/reset the deliver state + app.deliverState = nil + + defer func() { + if app.haltHeight > 0 && uint64(header.Height) == app.haltHeight { + app.logger.Info("halting node per configuration", "height", app.haltHeight) + os.Exit(0) + } + }() + + return abci.ResponseCommit{ + Data: commitID.Hash, + } +} + +// Query implements the ABCI interface. It delegates to CommitMultiStore if it +// implements Queryable. +func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { + path := splitPath(req.Path) + if len(path) == 0 { + msg := "no query path provided" + return sdk.ErrUnknownRequest(msg).QueryResult() + } + + switch path[0] { + // "/app" prefix for special application queries + case "app": + return handleQueryApp(app, path, req) + + case "store": + return handleQueryStore(app, path, req) + + case "p2p": + return handleQueryP2P(app, path, req) + + case "custom": + return handleQueryCustom(app, path, req) + } + + msg := "unknown query path" + return sdk.ErrUnknownRequest(msg).QueryResult() +} + +func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { + if len(path) >= 2 { + 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(txBytes, tx) + } + + case "version": + return abci.ResponseQuery{ + Code: uint32(sdk.CodeOK), + Codespace: string(sdk.CodespaceRoot), + Height: req.Height, + Value: []byte(app.appVersion), + } + + default: + result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() + } + + value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) + return abci.ResponseQuery{ + Code: uint32(sdk.CodeOK), + Codespace: string(sdk.CodespaceRoot), + Height: req.Height, + Value: value, + } + } + + msg := "Expected second parameter to be either simulate or version, neither was present" + return sdk.ErrUnknownRequest(msg).QueryResult() +} + +func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { + // "/store" prefix for store queries + 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:], "/") + + // when a client did not provide a query height, manually inject the latest + if req.Height == 0 { + req.Height = app.LastBlockHeight() + } + + if req.Height <= 1 && req.Prove { + return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult() + } + + resp := queryable.Query(req) + resp.Height = req.Height + + return resp +} + +func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.ResponseQuery) { + // "/p2p" prefix for p2p queries + if len(path) >= 4 { + cmd, typ, arg := path[1], path[2], path[3] + switch cmd { + case "filter": + switch typ { + case "addr": + return app.FilterPeerByAddrPort(arg) + + case "id": + return app.FilterPeerByID(arg) + } + + default: + msg := "Expected second parameter to be filter" + return sdk.ErrUnknownRequest(msg).QueryResult() + } + } + + msg := "Expected path is p2p filter " + return sdk.ErrUnknownRequest(msg).QueryResult() +} + +func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { + // path[0] should be "custom" because "/custom" prefix is required for keeper + // queries. + // + // The queryRouter routes using path[1]. For example, in the path + // "custom/gov/proposal", queryRouter routes using "gov". + if len(path) < 2 || path[1] == "" { + return sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() + } + + querier := app.queryRouter.Route(path[1]) + if querier == nil { + return sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() + } + + // when a client did not provide a query height, manually inject the latest + if req.Height == 0 { + req.Height = app.LastBlockHeight() + } + + if req.Height <= 1 && req.Prove { + return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult() + } + + cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height) + if err != nil { + return sdk.ErrInternal( + fmt.Sprintf( + "failed to load state at height %d; %s (latest height: %d)", + req.Height, err, app.LastBlockHeight(), + ), + ).QueryResult() + } + + // cache wrap the commit-multistore for safety + ctx := sdk.NewContext( + cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, + ).WithMinGasPrices(app.minGasPrices) + + // Passes the rest of the path as an argument to the querier. + // + // For example, in the path "custom/gov/proposal/test", the gov querier gets + // []string{"proposal", "test"} as the path. + resBytes, queryErr := querier(ctx, path[2:], req) + if queryErr != nil { + return abci.ResponseQuery{ + Code: uint32(queryErr.Code()), + Codespace: string(queryErr.Codespace()), + Height: req.Height, + Log: queryErr.ABCILog(), + } + } + + return abci.ResponseQuery{ + Code: uint32(sdk.CodeOK), + Height: req.Height, + Value: resBytes, + } +} diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index d336e79fca25..f06f7bf4a574 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -2,18 +2,15 @@ package baseapp import ( "encoding/json" + "errors" "fmt" "io/ioutil" "os" "reflect" "runtime/debug" - "sort" "strings" - "errors" - "github.com/gogo/protobuf/proto" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" @@ -25,12 +22,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Key to store the consensus params in the main store. -var mainConsensusParamsKey = []byte("consensus_params") - -// Enum mode for app.runTx -type runTxMode uint8 - const ( // Check a transaction runTxModeCheck runTxMode = iota @@ -43,11 +34,24 @@ const ( MainStoreKey = "main" ) -// StoreLoader defines a customizable function to control how we load the CommitMultiStore -// from disk. This is useful for state migration, when loading a datastore written with -// an older version of the software. In particular, if a module changed the substore key name -// (or removed a substore) between two versions of the software. -type StoreLoader func(ms sdk.CommitMultiStore) error +var ( + _ abci.Application = (*BaseApp)(nil) + + // mainConsensusParamsKey defines a key to store the consensus params in the + // main store. + mainConsensusParamsKey = []byte("consensus_params") +) + +type ( + // Enum mode for app.runTx + runTxMode uint8 + + // StoreLoader defines a customizable function to control how we load the CommitMultiStore + // from disk. This is useful for state migration, when loading a datastore written with + // an older version of the software. In particular, if a module changed the substore key name + // (or removed a substore) between two versions of the software. + StoreLoader func(ms sdk.CommitMultiStore) error +) // BaseApp reflects the ABCI application implementation. type BaseApp struct { @@ -99,8 +103,6 @@ type BaseApp struct { appVersion string } -var _ abci.Application = (*BaseApp)(nil) - // NewBaseApp returns a reference to an initialized BaseApp. It accepts a // variadic number of option functions, which act on the BaseApp to set // configuration choices. @@ -156,8 +158,10 @@ func (app *BaseApp) MountStores(keys ...sdk.StoreKey) { // retain history, but it's useful for faster simulation. app.MountStore(key, sdk.StoreTypeDB) } + case *sdk.TransientStoreKey: app.MountStore(key, sdk.StoreTypeTransient) + default: panic("Unrecognized store key type " + reflect.TypeOf(key).Name()) } @@ -411,275 +415,6 @@ func (app *BaseApp) getMaximumBlockGas() uint64 { } } -// ---------------------------------------------------------------------------- -// ABCI - -// Info implements the ABCI interface. -func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { - lastCommitID := app.cms.LastCommitID() - - return abci.ResponseInfo{ - Data: app.name, - LastBlockHeight: lastCommitID.Version, - LastBlockAppHash: lastCommitID.Hash, - } -} - -// SetOption implements the ABCI interface. -func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) { - // TODO: Implement! - return -} - -// InitChain implements the ABCI interface. It runs the initialization logic -// directly on the CommitMultiStore. -func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { - // stash the consensus params in the cms main store and memoize - if req.ConsensusParams != nil { - app.setConsensusParams(req.ConsensusParams) - app.storeConsensusParams(req.ConsensusParams) - } - - initHeader := abci.Header{ChainID: req.ChainId, Time: req.Time} - - // initialize the deliver state and check state with a correct header - app.setDeliverState(initHeader) - app.setCheckState(initHeader) - - if app.initChainer == nil { - return - } - - // add block gas meter for any genesis transactions (allow infinite gas) - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewInfiniteGasMeter()) - - res = app.initChainer(app.deliverState.ctx, req) - - // sanity check - if len(req.Validators) > 0 { - if len(req.Validators) != len(res.Validators) { - panic(fmt.Errorf( - "len(RequestInitChain.Validators) != len(validators) (%d != %d)", - len(req.Validators), len(res.Validators))) - } - sort.Sort(abci.ValidatorUpdates(req.Validators)) - sort.Sort(abci.ValidatorUpdates(res.Validators)) - for i, val := range res.Validators { - if !val.Equal(req.Validators[i]) { - panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i)) - } - } - } - - // NOTE: We don't commit, but BeginBlock for block 1 starts from this - // deliverState. - return -} - -// FilterPeerByAddrPort filters peers by address/port. -func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { - if app.addrPeerFilter != nil { - return app.addrPeerFilter(info) - } - return abci.ResponseQuery{} -} - -// FilterPeerByIDfilters peers by node ID. -func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { - if app.idPeerFilter != nil { - return app.idPeerFilter(info) - } - return abci.ResponseQuery{} -} - -// Splits a string path using the delimiter '/'. -// e.g. "this/is/funny" becomes []string{"this", "is", "funny"} -func splitPath(requestPath string) (path []string) { - path = strings.Split(requestPath, "/") - // first element is empty string - if len(path) > 0 && path[0] == "" { - path = path[1:] - } - return path -} - -// Query implements the ABCI interface. It delegates to CommitMultiStore if it -// implements Queryable. -func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - path := splitPath(req.Path) - if len(path) == 0 { - msg := "no query path provided" - return sdk.ErrUnknownRequest(msg).QueryResult() - } - - switch path[0] { - // "/app" prefix for special application queries - case "app": - return handleQueryApp(app, path, req) - - case "store": - return handleQueryStore(app, path, req) - - case "p2p": - return handleQueryP2P(app, path, req) - - case "custom": - return handleQueryCustom(app, path, req) - } - - msg := "unknown query path" - return sdk.ErrUnknownRequest(msg).QueryResult() -} - -func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { - if len(path) >= 2 { - 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(txBytes, tx) - } - - case "version": - return abci.ResponseQuery{ - Code: uint32(sdk.CodeOK), - Codespace: string(sdk.CodespaceRoot), - Height: req.Height, - Value: []byte(app.appVersion), - } - - default: - result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() - } - - value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) - return abci.ResponseQuery{ - Code: uint32(sdk.CodeOK), - Codespace: string(sdk.CodespaceRoot), - Height: req.Height, - Value: value, - } - } - - msg := "Expected second parameter to be either simulate or version, neither was present" - return sdk.ErrUnknownRequest(msg).QueryResult() -} - -func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { - // "/store" prefix for store queries - 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:], "/") - - // when a client did not provide a query height, manually inject the latest - if req.Height == 0 { - req.Height = app.LastBlockHeight() - } - - if req.Height <= 1 && req.Prove { - return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult() - } - - resp := queryable.Query(req) - resp.Height = req.Height - - return resp -} - -func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.ResponseQuery) { - // "/p2p" prefix for p2p queries - if len(path) >= 4 { - cmd, typ, arg := path[1], path[2], path[3] - switch cmd { - case "filter": - switch typ { - case "addr": - return app.FilterPeerByAddrPort(arg) - - case "id": - return app.FilterPeerByID(arg) - } - - default: - msg := "Expected second parameter to be filter" - return sdk.ErrUnknownRequest(msg).QueryResult() - } - } - - msg := "Expected path is p2p filter " - return sdk.ErrUnknownRequest(msg).QueryResult() -} - -func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { - // path[0] should be "custom" because "/custom" prefix is required for keeper - // queries. - // - // The queryRouter routes using path[1]. For example, in the path - // "custom/gov/proposal", queryRouter routes using "gov". - if len(path) < 2 || path[1] == "" { - return sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() - } - - querier := app.queryRouter.Route(path[1]) - if querier == nil { - return sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() - } - - // when a client did not provide a query height, manually inject the latest - if req.Height == 0 { - req.Height = app.LastBlockHeight() - } - - if req.Height <= 1 && req.Prove { - return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult() - } - - cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height) - if err != nil { - return sdk.ErrInternal( - fmt.Sprintf( - "failed to load state at height %d; %s (latest height: %d)", - req.Height, err, app.LastBlockHeight(), - ), - ).QueryResult() - } - - // cache wrap the commit-multistore for safety - ctx := sdk.NewContext( - cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, - ).WithMinGasPrices(app.minGasPrices) - - // Passes the rest of the path as an argument to the querier. - // - // For example, in the path "custom/gov/proposal/test", the gov querier gets - // []string{"proposal", "test"} as the path. - resBytes, queryErr := querier(ctx, path[2:], req) - if queryErr != nil { - return abci.ResponseQuery{ - Code: uint32(queryErr.Code()), - Codespace: string(queryErr.Codespace()), - Height: req.Height, - Log: queryErr.ABCILog(), - } - } - - return abci.ResponseQuery{ - Code: uint32(sdk.CodeOK), - Height: req.Height, - Value: resBytes, - } -} - func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error { if req.Header.Height < 1 { return fmt.Errorf("invalid height: %d", req.Header.Height) @@ -693,97 +428,6 @@ func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error { return nil } -// BeginBlock implements the ABCI application interface. -func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - if app.cms.TracingEnabled() { - app.cms.SetTracingContext(sdk.TraceContext( - map[string]interface{}{"blockHeight": req.Header.Height}, - )) - } - - if err := app.validateHeight(req); err != nil { - panic(err) - } - - // Initialize the DeliverTx state. If this is the first block, it should - // already be initialized in InitChain. Otherwise app.deliverState will be - // nil, since it is reset on Commit. - if app.deliverState == nil { - app.setDeliverState(req.Header) - } else { - // In the first block, app.deliverState.ctx will already be initialized - // by InitChain. Context is now updated with Header information. - app.deliverState.ctx = app.deliverState.ctx. - WithBlockHeader(req.Header). - WithBlockHeight(req.Header.Height) - } - - // add block gas meter - var gasMeter sdk.GasMeter - if maxGas := app.getMaximumBlockGas(); maxGas > 0 { - gasMeter = sdk.NewGasMeter(maxGas) - } else { - gasMeter = sdk.NewInfiniteGasMeter() - } - - app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter) - - if app.beginBlocker != nil { - res = app.beginBlocker(app.deliverState.ctx, req) - } - - // set the signed validators for addition to context in deliverTx - app.voteInfos = req.LastCommitInfo.GetVotes() - return -} - -// CheckTx implements the ABCI interface. It runs the "basic checks" to see -// whether or not a transaction can possibly be executed, first decoding and then -// the ante handler (which checks signatures/fees/ValidateBasic). -// -// NOTE:CheckTx does not run the actual Msg handler function(s). -func (app *BaseApp) CheckTx(req abci.RequestCheckTx) (res abci.ResponseCheckTx) { - var result sdk.Result - - tx, err := app.txDecoder(req.Tx) - if err != nil { - result = err.Result() - } else { - result = app.runTx(runTxModeCheck, req.Tx, tx) - } - - return abci.ResponseCheckTx{ - Code: uint32(result.Code), - Data: result.Data, - Log: result.Log, - GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? - GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? - Events: result.Events.ToABCIEvents(), - } -} - -// DeliverTx implements the ABCI interface. -func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) { - var result sdk.Result - - tx, err := app.txDecoder(req.Tx) - if err != nil { - result = err.Result() - } else { - result = app.runTx(runTxModeDeliver, req.Tx, tx) - } - - return abci.ResponseDeliverTx{ - Code: uint32(result.Code), - Codespace: string(result.Codespace), - Data: result.Data, - Log: result.Log, - GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? - GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? - Events: result.Events.ToABCIEvents(), - } -} - // validateBasicTxMsgs executes basic validator calls for messages. func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { if msgs == nil || len(msgs) == 0 { @@ -1030,67 +674,13 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return result } -// EndBlock implements the ABCI interface. -func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { - if app.deliverState.ms.TracingEnabled() { - app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) - } - - if app.endBlocker != nil { - res = app.endBlocker(app.deliverState.ctx, req) - } - - return -} - -// Commit implements the ABCI interface. It will commit all state that exists in -// the deliver state's multi-store and includes the resulting commit ID in the -// returned abci.ResponseCommit. Commit will set the check state based on the -// latest header and reset the deliver state. Also, if a non-zero halt height is -// defined in config, Commit will execute a deferred function call to check -// against that height and gracefully halt if it matches the latest committed -// height. -func (app *BaseApp) Commit() (res abci.ResponseCommit) { - header := app.deliverState.ctx.BlockHeader() - - // write the Deliver state and commit the MultiStore - app.deliverState.ms.Write() - commitID := app.cms.Commit() - app.logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID)) - - // Reset the Check state to the latest committed. - // - // NOTE: This is safe because Tendermint holds a lock on the mempool for - // Commit. Use the header from this latest block. - app.setCheckState(header) - - // empty/reset the deliver state - app.deliverState = nil - - defer func() { - if app.haltHeight > 0 && uint64(header.Height) == app.haltHeight { - app.logger.Info("halting node per configuration", "height", app.haltHeight) - os.Exit(0) - } - }() - - return abci.ResponseCommit{ - Data: commitID.Hash, +// Splits a string path using the delimiter '/'. +// e.g. "this/is/funny" becomes []string{"this", "is", "funny"} +func splitPath(requestPath string) (path []string) { + path = strings.Split(requestPath, "/") + // first element is empty string + if len(path) > 0 && path[0] == "" { + path = path[1:] } -} - -// ---------------------------------------------------------------------------- -// State - -type state struct { - ms sdk.CacheMultiStore - ctx sdk.Context -} - -func (st *state) CacheMultiStore() sdk.CacheMultiStore { - return st.ms.CacheMultiStore() -} - -func (st *state) Context() sdk.Context { - return st.ctx + return path } diff --git a/baseapp/doc.go b/baseapp/doc.go deleted file mode 100644 index d708d08bcd1f..000000000000 --- a/baseapp/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package baseapp contains data structures that provide basic data storage -functionality and act as a bridge between the ABCI interface and the SDK -abstractions. - -BaseApp has no state except the CommitMultiStore you provide upon init. -*/ -package baseapp diff --git a/baseapp/state.go b/baseapp/state.go new file mode 100644 index 000000000000..addc89cb342c --- /dev/null +++ b/baseapp/state.go @@ -0,0 +1,21 @@ +package baseapp + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type state struct { + ms sdk.CacheMultiStore + ctx sdk.Context +} + +// CacheMultiStore calls and returns a CacheMultiStore on the state's underling +// CacheMultiStore. +func (st *state) CacheMultiStore() sdk.CacheMultiStore { + return st.ms.CacheMultiStore() +} + +// Context returns the Context of the state. +func (st *state) Context() sdk.Context { + return st.ctx +}