Merge PR #3468: Increase BaseApp Test Coverage
This commit is contained in:
committed by
Christopher Goes
parent
1766a73a4c
commit
52350c592b
@@ -75,6 +75,8 @@ IMPROVEMENTS
|
||||
* [\#2605] x/params add subkey accessing
|
||||
* [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor
|
||||
* \#3435 Test that store implementations do not allow nil values
|
||||
* [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp`
|
||||
test coverage.
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
||||
+141
-103
@@ -52,7 +52,6 @@ type BaseApp struct {
|
||||
// set upon LoadVersion or LoadLatestVersion.
|
||||
mainKey *sdk.KVStoreKey // Main KVStore in cms
|
||||
|
||||
// may be nil
|
||||
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
||||
initChainer sdk.InitChainer // initialize state with validators and state blob
|
||||
beginBlocker sdk.BeginBlocker // logic to run before any txs
|
||||
@@ -61,8 +60,8 @@ type BaseApp struct {
|
||||
pubkeyPeerFilter sdk.PeerFilter // filter peers by public key
|
||||
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.
|
||||
|
||||
//--------------------
|
||||
// Volatile
|
||||
// --------------------
|
||||
// Volatile state
|
||||
// checkState is set on initialization and reset on Commit.
|
||||
// deliverState is set in InitChain and BeginBlock and cleared on Commit.
|
||||
// See methods setCheckState and setDeliverState.
|
||||
@@ -71,25 +70,28 @@ type BaseApp struct {
|
||||
voteInfos []abci.VoteInfo // absent validators from begin block
|
||||
|
||||
// consensus params
|
||||
// TODO move this in the future to baseapp param store on main store.
|
||||
// TODO: Move this in the future to baseapp param store on main store.
|
||||
consensusParams *abci.ConsensusParams
|
||||
|
||||
// The minimum gas prices a validator is willing to accept for processing a
|
||||
// transaction. This is mainly used for DoS and spam prevention.
|
||||
minGasPrices sdk.DecCoins
|
||||
|
||||
// flag for sealing
|
||||
// flag for sealing options and parameters to a BaseApp
|
||||
sealed bool
|
||||
}
|
||||
|
||||
var _ abci.Application = (*BaseApp)(nil)
|
||||
|
||||
// NewBaseApp returns a reference to an initialized BaseApp.
|
||||
// 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.
|
||||
//
|
||||
// NOTE: The db is used to store the version number for now.
|
||||
// Accepts a user-defined txDecoder
|
||||
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
|
||||
func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp)) *BaseApp {
|
||||
func NewBaseApp(
|
||||
name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp),
|
||||
) *BaseApp {
|
||||
|
||||
app := &BaseApp{
|
||||
Logger: logger,
|
||||
name: name,
|
||||
@@ -103,10 +105,11 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod
|
||||
for _, option := range options {
|
||||
option(app)
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// BaseApp Name
|
||||
// Name returns the name of the BaseApp.
|
||||
func (app *BaseApp) Name() string {
|
||||
return app.name
|
||||
}
|
||||
@@ -117,7 +120,8 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) {
|
||||
app.cms.SetTracer(w)
|
||||
}
|
||||
|
||||
// Mount IAVL or DB stores to the provided keys in the BaseApp multistore
|
||||
// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp
|
||||
// multistore.
|
||||
func (app *BaseApp) MountStores(keys ...*sdk.KVStoreKey) {
|
||||
for _, key := range keys {
|
||||
if !app.fauxMerkleMode {
|
||||
@@ -130,25 +134,28 @@ func (app *BaseApp) MountStores(keys ...*sdk.KVStoreKey) {
|
||||
}
|
||||
}
|
||||
|
||||
// Mount stores to the provided keys in the BaseApp multistore
|
||||
// MountStoresTransient mounts transient stores to the provided keys in the
|
||||
// BaseApp multistore.
|
||||
func (app *BaseApp) MountStoresTransient(keys ...*sdk.TransientStoreKey) {
|
||||
for _, key := range keys {
|
||||
app.MountStore(key, sdk.StoreTypeTransient)
|
||||
}
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore, using a specified DB
|
||||
// MountStoreWithDB mounts a store to the provided key in the BaseApp
|
||||
// multistore, using a specified DB.
|
||||
func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
|
||||
app.cms.MountStoreWithDB(key, typ, db)
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore, using the default DB
|
||||
// MountStore mounts a store to the provided key in the BaseApp multistore,
|
||||
// using the default DB.
|
||||
func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
|
||||
app.cms.MountStoreWithDB(key, typ, nil)
|
||||
}
|
||||
|
||||
// load latest application version
|
||||
// panics if called more than once on a running baseapp
|
||||
// LoadLatestVersion loads the latest application version. It will panic if
|
||||
// called more than once on a running BaseApp.
|
||||
func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error {
|
||||
err := app.cms.LoadLatestVersion()
|
||||
if err != nil {
|
||||
@@ -157,8 +164,8 @@ func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error {
|
||||
return app.initFromMainStore(mainKey)
|
||||
}
|
||||
|
||||
// load application version
|
||||
// panics if called more than once on a running baseapp
|
||||
// LoadVersion loads the BaseApp application version. It will panic if called
|
||||
// more than once on a running baseapp.
|
||||
func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error {
|
||||
err := app.cms.LoadVersion(version)
|
||||
if err != nil {
|
||||
@@ -167,20 +174,18 @@ func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error {
|
||||
return app.initFromMainStore(mainKey)
|
||||
}
|
||||
|
||||
// the last CommitID of the multistore
|
||||
// LastCommitID returns the last CommitID of the multistore.
|
||||
func (app *BaseApp) LastCommitID() sdk.CommitID {
|
||||
return app.cms.LastCommitID()
|
||||
}
|
||||
|
||||
// the last committed block height
|
||||
// LastBlockHeight returns the last committed block height.
|
||||
func (app *BaseApp) LastBlockHeight() int64 {
|
||||
return app.cms.LastCommitID().Version
|
||||
}
|
||||
|
||||
// initializes the remaining logic from app.cms
|
||||
func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error {
|
||||
|
||||
// main store should exist.
|
||||
mainStore := app.cms.GetKVStore(mainKey)
|
||||
if mainStore == nil {
|
||||
return errors.New("baseapp expects MultiStore with 'main' KVStore")
|
||||
@@ -192,23 +197,24 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error {
|
||||
}
|
||||
app.mainKey = mainKey
|
||||
|
||||
// load consensus params from the main store
|
||||
// Load the consensus params from the main store. If the consensus params are
|
||||
// nil, it will be saved later during InitChain.
|
||||
//
|
||||
// TODO: assert that InitChain hasn't yet been called.
|
||||
consensusParamsBz := mainStore.Get(mainConsensusParamsKey)
|
||||
if consensusParamsBz != nil {
|
||||
var consensusParams = &abci.ConsensusParams{}
|
||||
|
||||
err := proto.Unmarshal(consensusParamsBz, consensusParams)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
app.setConsensusParams(consensusParams)
|
||||
} else {
|
||||
// It will get saved later during InitChain.
|
||||
// TODO assert that InitChain hasn't yet been called.
|
||||
}
|
||||
|
||||
// Needed for `gaiad export`, which inits from store but never calls initchain
|
||||
// needed for `gaiad export`, which inits from store but never calls initchain
|
||||
app.setCheckState(abci.Header{})
|
||||
|
||||
app.Seal()
|
||||
|
||||
return nil
|
||||
@@ -218,7 +224,8 @@ func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) {
|
||||
app.minGasPrices = gasPrices
|
||||
}
|
||||
|
||||
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
|
||||
// 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, app.Logger).
|
||||
@@ -228,18 +235,24 @@ func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, app.Logger)
|
||||
}
|
||||
|
||||
type state struct {
|
||||
ms sdk.CacheMultiStore
|
||||
ctx sdk.Context
|
||||
// Router returns the router of the BaseApp.
|
||||
func (app *BaseApp) Router() Router {
|
||||
if app.sealed {
|
||||
// We cannot return a router when the app is sealed because we can't have
|
||||
// any routes modified which would cause unexpected routing behavior.
|
||||
panic("Router() on sealed BaseApp")
|
||||
}
|
||||
return app.router
|
||||
}
|
||||
|
||||
func (st *state) CacheMultiStore() sdk.CacheMultiStore {
|
||||
return st.ms.CacheMultiStore()
|
||||
}
|
||||
// QueryRouter returns the QueryRouter of a BaseApp.
|
||||
func (app *BaseApp) QueryRouter() QueryRouter { return app.queryRouter }
|
||||
|
||||
func (st *state) Context() sdk.Context {
|
||||
return st.ctx
|
||||
}
|
||||
// Seal seals a BaseApp. It prohibits any further modifications to a BaseApp.
|
||||
func (app *BaseApp) Seal() { app.sealed = true }
|
||||
|
||||
// IsSealed returns true if the BaseApp is sealed and false otherwise.
|
||||
func (app *BaseApp) IsSealed() bool { return app.sealed }
|
||||
|
||||
func (app *BaseApp) setCheckState(header abci.Header) {
|
||||
ms := app.cms.CacheMultiStore()
|
||||
@@ -280,11 +293,10 @@ func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) {
|
||||
return uint64(app.consensusParams.BlockSize.MaxGas)
|
||||
}
|
||||
|
||||
//______________________________________________________________________________
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ABCI
|
||||
|
||||
// Implements ABCI
|
||||
// Info implements the ABCI interface.
|
||||
func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
|
||||
lastCommitID := app.cms.LastCommitID()
|
||||
|
||||
@@ -295,23 +307,23 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// SetOption implements the ABCI interface.
|
||||
func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) {
|
||||
// TODO: Implement
|
||||
// TODO: Implement!
|
||||
return
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// InitChain runs the initialization logic directly on the CommitMultiStore.
|
||||
// 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.
|
||||
// stash the consensus params in the cms main store and memoize
|
||||
if req.ConsensusParams != nil {
|
||||
app.setConsensusParams(req.ConsensusParams)
|
||||
app.storeConsensusParams(req.ConsensusParams)
|
||||
}
|
||||
|
||||
// Initialize the deliver state and check state with ChainID and run initChain
|
||||
// initialize the deliver state and check state with ChainID and run initChain
|
||||
app.setDeliverState(abci.Header{ChainID: req.ChainId})
|
||||
app.setCheckState(abci.Header{ChainID: req.ChainId})
|
||||
|
||||
@@ -325,12 +337,12 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
||||
|
||||
res = app.initChainer(app.deliverState.ctx, req)
|
||||
|
||||
// NOTE: we don't commit, but BeginBlock for block 1
|
||||
// starts from this deliverState
|
||||
// NOTE: We don't commit, but BeginBlock for block 1 starts from this
|
||||
// deliverState.
|
||||
return
|
||||
}
|
||||
|
||||
// Filter peers by address / port
|
||||
// FilterPeerByAddrPort filters peers by address/port.
|
||||
func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
|
||||
if app.addrPeerFilter != nil {
|
||||
return app.addrPeerFilter(info)
|
||||
@@ -338,7 +350,7 @@ func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
|
||||
return abci.ResponseQuery{}
|
||||
}
|
||||
|
||||
// Filter peers by public key
|
||||
// FilterPeerByPubKey filters peers by a public key.
|
||||
func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery {
|
||||
if app.pubkeyPeerFilter != nil {
|
||||
return app.pubkeyPeerFilter(info)
|
||||
@@ -346,7 +358,8 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery {
|
||||
return abci.ResponseQuery{}
|
||||
}
|
||||
|
||||
// Splits a string path using the delimter '/'. i.e. "this/is/funny" becomes []string{"this", "is", "funny"}
|
||||
// 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
|
||||
@@ -356,22 +369,26 @@ func splitPath(requestPath string) (path []string) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Implements ABCI.
|
||||
// Delegates to CommitMultiStore if it implements Queryable
|
||||
// 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)
|
||||
}
|
||||
@@ -383,6 +400,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
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
|
||||
@@ -392,17 +410,18 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
|
||||
} else {
|
||||
result = app.Simulate(txBytes, tx)
|
||||
}
|
||||
|
||||
case "version":
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
Codespace: string(sdk.CodespaceRoot),
|
||||
Value: []byte(version.Version),
|
||||
}
|
||||
|
||||
default:
|
||||
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
|
||||
}
|
||||
|
||||
// Encode with json
|
||||
value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result)
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
@@ -410,6 +429,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
msg := "Expected second parameter to be either simulate or version, neither was present"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
@@ -421,12 +441,12 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) (res a
|
||||
msg := "multistore doesn't support queries"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
|
||||
req.Path = "/" + strings.Join(path[1:], "/")
|
||||
return queryable.Query(req)
|
||||
}
|
||||
|
||||
// nolint: unparam
|
||||
func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
// "/p2p" prefix for p2p queries
|
||||
if len(path) >= 4 {
|
||||
if path[1] == "filter" {
|
||||
@@ -449,23 +469,29 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc
|
||||
}
|
||||
|
||||
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"
|
||||
// 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()
|
||||
}
|
||||
|
||||
// Cache wrap the commit-multistore for safety.
|
||||
// cache wrap the commit-multistore for safety
|
||||
ctx := sdk.NewContext(
|
||||
app.cms.CacheMultiStore(), 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
|
||||
//
|
||||
// For example, in the path "custom/gov/proposal/test", the gov querier gets
|
||||
// []string{"proposal", "test"} as the path.
|
||||
resBytes, err := querier(ctx, path[2:], req)
|
||||
if err != nil {
|
||||
return abci.ResponseQuery{
|
||||
@@ -474,6 +500,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
||||
Log: err.ABCILog(),
|
||||
}
|
||||
}
|
||||
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
Value: resBytes,
|
||||
@@ -521,15 +548,16 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
|
||||
return
|
||||
}
|
||||
|
||||
// CheckTx implements ABCI
|
||||
// CheckTx runs the "basic checks" to see whether or not a transaction can possibly be executed,
|
||||
// first decoding, then the ante handler (which checks signatures/fees/ValidateBasic),
|
||||
// then finally the route match to see whether a handler exists. CheckTx does not run the actual
|
||||
// Msg handler function(s).
|
||||
// CheckTx implements the ABCI interface. It runs the "basic checks" to see
|
||||
// whether or not a transaction can possibly be executed, first decoding, then
|
||||
// the ante handler (which checks signatures/fees/ValidateBasic), then finally
|
||||
// the route match to see whether a handler exists.
|
||||
//
|
||||
// NOTE:CheckTx does not run the actual Msg handler function(s).
|
||||
func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
|
||||
// Decode the Tx.
|
||||
var result sdk.Result
|
||||
var tx, err = app.txDecoder(txBytes)
|
||||
|
||||
tx, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
@@ -546,22 +574,17 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// DeliverTx implements the ABCI interface.
|
||||
func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
||||
|
||||
// Decode the Tx.
|
||||
var tx, err = app.txDecoder(txBytes)
|
||||
var result sdk.Result
|
||||
|
||||
tx, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.runTx(runTxModeDeliver, txBytes, tx)
|
||||
}
|
||||
|
||||
// Even though the Result.Code is not OK, there are still effects,
|
||||
// namely fee deductions and sequence incrementing.
|
||||
|
||||
// Tell the blockchain engine (i.e. Tendermint).
|
||||
return abci.ResponseDeliverTx{
|
||||
Code: uint32(result.Code),
|
||||
Codespace: string(result.Codespace),
|
||||
@@ -573,10 +596,10 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Basic validator for msgs
|
||||
// validateBasicTxMsgs executes basic validator calls for messages.
|
||||
func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error {
|
||||
if msgs == nil || len(msgs) == 0 {
|
||||
// TODO: probably shouldn't be ErrInternal. Maybe new ErrInvalidMessage, or ?
|
||||
// TODO: Probably shouldn't be ErrInternal. Maybe ErrInvalidMessage?
|
||||
return sdk.ErrInternal("Tx.GetMsgs() must return at least one message in list")
|
||||
}
|
||||
|
||||
@@ -597,22 +620,25 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Con
|
||||
WithTxBytes(txBytes).
|
||||
WithVoteInfos(app.voteInfos).
|
||||
WithConsensusParams(app.consensusParams)
|
||||
|
||||
if mode == runTxModeSimulate {
|
||||
ctx, _ = ctx.CacheContext()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Iterates through msgs and executes them
|
||||
// runMsgs iterates through all the messages and executes them.
|
||||
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
|
||||
// accumulate results
|
||||
logs := make([]string, 0, len(msgs))
|
||||
|
||||
var data []byte // NOTE: we just append them all (?!)
|
||||
var tags sdk.Tags // also just append them all
|
||||
var code sdk.CodeType
|
||||
var codespace sdk.CodespaceType
|
||||
|
||||
for msgIdx, msg := range msgs {
|
||||
// Match route.
|
||||
// match message route
|
||||
msgRoute := msg.Route()
|
||||
handler := app.router.Route(msgRoute)
|
||||
if handler == nil {
|
||||
@@ -620,20 +646,19 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
|
||||
}
|
||||
|
||||
var msgResult sdk.Result
|
||||
// Skip actual execution for CheckTx
|
||||
|
||||
// skip actual execution for CheckTx mode
|
||||
if mode != runTxModeCheck {
|
||||
msgResult = handler(ctx, msg)
|
||||
}
|
||||
|
||||
// NOTE: GasWanted is determined by ante handler and
|
||||
// GasUsed by the GasMeter
|
||||
// NOTE: GasWanted is determined by ante handler and GasUsed by the GasMeter.
|
||||
|
||||
// Append Data and Tags
|
||||
data = append(data, msgResult.Data...)
|
||||
tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type()))
|
||||
tags = append(tags, msgResult.Tags...)
|
||||
|
||||
// Stop execution and return on first failed message.
|
||||
// stop execution and return on first failed message
|
||||
if !msgResult.IsOK() {
|
||||
logs = append(logs, fmt.Sprintf("Msg %d failed: %s", msgIdx, msgResult.Log))
|
||||
code = msgResult.Code
|
||||
@@ -641,11 +666,10 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
|
||||
break
|
||||
}
|
||||
|
||||
// Construct usable logs in multi-message transactions.
|
||||
// construct usable logs in multi-message transactions
|
||||
logs = append(logs, fmt.Sprintf("Msg %d: %s", msgIdx, msgResult.Log))
|
||||
}
|
||||
|
||||
// Set the final gas values.
|
||||
result = sdk.Result{
|
||||
Code: code,
|
||||
Codespace: codespace,
|
||||
@@ -733,11 +757,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
result.GasUsed = ctx.GasMeter().GasConsumed()
|
||||
}()
|
||||
|
||||
// If BlockGasMeter() panics it will be caught by the above recover and
|
||||
// return an error - in any case BlockGasMeter will consume gas past
|
||||
// the limit.
|
||||
// NOTE: this must exist in a separate defer function for the
|
||||
// above recovery to recover from this one
|
||||
// If BlockGasMeter() panics it will be caught by the above recover and will
|
||||
// return an error - in any case BlockGasMeter will consume gas past the limit.
|
||||
//
|
||||
// NOTE: This must exist in a separate defer function for the above recovery
|
||||
// to recover from this one.
|
||||
defer func() {
|
||||
if mode == runTxModeDeliver {
|
||||
ctx.BlockGasMeter().ConsumeGas(
|
||||
@@ -756,7 +780,6 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// Execute the ante handler if one is defined.
|
||||
if app.anteHandler != nil {
|
||||
var anteCtx sdk.Context
|
||||
var msCache sdk.CacheMultiStore
|
||||
@@ -812,7 +835,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
return
|
||||
}
|
||||
|
||||
// EndBlock implements the ABCI application interface.
|
||||
// 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)
|
||||
@@ -825,27 +848,42 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
|
||||
return
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// Commit implements the ABCI interface.
|
||||
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
||||
header := app.deliverState.ctx.BlockHeader()
|
||||
|
||||
// Write the Deliver state and commit the MultiStore
|
||||
// write the Deliver state and commit the MultiStore
|
||||
app.deliverState.ms.Write()
|
||||
commitID := app.cms.Commit()
|
||||
// TODO: this is missing a module identifier and dumps byte array
|
||||
app.Logger.Debug("Commit synced",
|
||||
"commit", fmt.Sprintf("%X", commitID),
|
||||
)
|
||||
|
||||
// Reset the Check state to the latest committed
|
||||
app.Logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID))
|
||||
|
||||
// Reset the Check state to the latest committed.
|
||||
//
|
||||
// NOTE: safe because Tendermint holds a lock on the mempool for Commit.
|
||||
// Use the header from this latest block.
|
||||
app.setCheckState(header)
|
||||
|
||||
// Empty the Deliver state
|
||||
// empty/reset the deliver state
|
||||
app.deliverState = nil
|
||||
|
||||
return abci.ResponseCommit{
|
||||
Data: commitID.Hash,
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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
|
||||
}
|
||||
|
||||
+246
-61
@@ -21,14 +21,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// make some cap keys
|
||||
capKey1 = sdk.NewKVStoreKey("key1")
|
||||
capKey2 = sdk.NewKVStoreKey("key2")
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// Helpers for setup. Most tests should be able to use setupBaseApp
|
||||
|
||||
func defaultLogger() log.Logger {
|
||||
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
}
|
||||
@@ -70,9 +66,6 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp {
|
||||
return app
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// test mounting and loading stores
|
||||
|
||||
func TestMountStores(t *testing.T) {
|
||||
app := setupBaseApp(t)
|
||||
|
||||
@@ -137,6 +130,41 @@ func TestLoadVersion(t *testing.T) {
|
||||
testLoadVersionHelper(t, app, int64(2), commitID2)
|
||||
}
|
||||
|
||||
func TestLoadVersionInvalid(t *testing.T) {
|
||||
logger := log.NewNopLogger()
|
||||
pruningOpt := SetPruning(store.PruneSyncable)
|
||||
db := dbm.NewMemDB()
|
||||
name := t.Name()
|
||||
app := NewBaseApp(name, logger, db, nil, pruningOpt)
|
||||
|
||||
capKey := sdk.NewKVStoreKey(MainStoreKey)
|
||||
app.MountStores(capKey)
|
||||
err := app.LoadLatestVersion(capKey)
|
||||
require.Nil(t, err)
|
||||
|
||||
// require error when loading an invalid version
|
||||
err = app.LoadVersion(-1, capKey)
|
||||
require.Error(t, err)
|
||||
|
||||
header := abci.Header{Height: 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res := app.Commit()
|
||||
commitID1 := sdk.CommitID{1, res.Data}
|
||||
|
||||
// create a new app with the stores mounted under the same cap key
|
||||
app = NewBaseApp(name, logger, db, nil, pruningOpt)
|
||||
app.MountStores(capKey)
|
||||
|
||||
// require we can load the latest version
|
||||
err = app.LoadVersion(1, capKey)
|
||||
require.Nil(t, err)
|
||||
testLoadVersionHelper(t, app, int64(1), commitID1)
|
||||
|
||||
// require error when loading an invalid version
|
||||
err = app.LoadVersion(2, capKey)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) {
|
||||
lastHeight := app.LastBlockHeight()
|
||||
lastID := app.LastCommitID()
|
||||
@@ -157,39 +185,21 @@ func testChangeNameHelper(name string) func(*BaseApp) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the app hash is static
|
||||
// TODO: https://github.com/cosmos/cosmos-sdk/issues/520
|
||||
/*func TestStaticAppHash(t *testing.T) {
|
||||
app := newBaseApp(t.Name())
|
||||
|
||||
// make a cap key and mount the store
|
||||
capKey := sdk.NewKVStoreKey(MainStoreKey)
|
||||
app.MountStores(capKey)
|
||||
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
require.Nil(t, err)
|
||||
|
||||
// execute some blocks
|
||||
header := abci.Header{Height: 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res := app.Commit()
|
||||
commitID1 := sdk.CommitID{1, res.Data}
|
||||
|
||||
header = abci.Header{Height: 2}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res = app.Commit()
|
||||
commitID2 := sdk.CommitID{2, res.Data}
|
||||
|
||||
require.Equal(t, commitID1.Hash, commitID2.Hash)
|
||||
}
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// test some basic abci/baseapp functionality
|
||||
|
||||
// Test that txs can be unmarshalled and read and that
|
||||
// correct error codes are returned when not
|
||||
func TestTxDecoder(t *testing.T) {
|
||||
// TODO
|
||||
codec := codec.New()
|
||||
registerTestCodec(codec)
|
||||
|
||||
app := newBaseApp(t.Name())
|
||||
tx := newTxCounter(1, 0)
|
||||
txBytes := codec.MustMarshalBinaryLengthPrefixed(tx)
|
||||
|
||||
dTx, err := app.txDecoder(txBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
cTx := dTx.(txTest)
|
||||
require.Equal(t, tx.Counter, cTx.Counter)
|
||||
}
|
||||
|
||||
// Test that Info returns the latest committed state.
|
||||
@@ -210,8 +220,46 @@ func TestInfo(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// InitChain, BeginBlock, EndBlock
|
||||
func TestBaseAppOptionSeal(t *testing.T) {
|
||||
app := setupBaseApp(t)
|
||||
|
||||
require.Panics(t, func() {
|
||||
app.SetName("")
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetDB(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetCMS(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetInitChainer(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetBeginBlocker(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetEndBlocker(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetAnteHandler(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetAddrPeerFilter(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetPubKeyPeerFilter(nil)
|
||||
})
|
||||
require.Panics(t, func() {
|
||||
app.SetFauxMerkleMode()
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetMinGasPrices(t *testing.T) {
|
||||
minGasPrices := sdk.DecCoins{sdk.NewDecCoin("stake", 5000)}
|
||||
app := newBaseApp(t.Name(), SetMinGasPrices(minGasPrices.String()))
|
||||
require.Equal(t, minGasPrices, app.minGasPrices)
|
||||
}
|
||||
|
||||
func TestInitChainer(t *testing.T) {
|
||||
name := t.Name()
|
||||
@@ -280,11 +328,6 @@ func TestInitChainer(t *testing.T) {
|
||||
require.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// Mock tx, msgs, and mapper for the baseapp tests.
|
||||
// Self-contained, just uses counters.
|
||||
// We don't care about signatures, coins, accounts, etc. in the baseapp.
|
||||
|
||||
// Simple tx with a list of Msgs.
|
||||
type txTest struct {
|
||||
Msgs []sdk.Msg
|
||||
@@ -417,9 +460,6 @@ func handlerMsgCounter(t *testing.T, capKey *sdk.KVStoreKey, deliverKey []byte)
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// simple int mapper
|
||||
|
||||
func i2b(i int64) []byte {
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
@@ -686,10 +726,6 @@ func TestSimulateTx(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
// Tx failure cases
|
||||
// TODO: add more
|
||||
|
||||
func TestRunInvalidTransaction(t *testing.T) {
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
@@ -777,11 +813,6 @@ func TestTxGasLimits(t *testing.T) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
|
||||
|
||||
// NOTE/TODO/XXX:
|
||||
// AnteHandlers must have their own defer/recover in order
|
||||
// for the BaseApp to know how much gas was used used!
|
||||
// This is because the GasMeter is created in the AnteHandler,
|
||||
// but if it panics the context won't be set properly in runTx's recover ...
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
@@ -866,11 +897,6 @@ func TestMaxBlockGasLimits(t *testing.T) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
|
||||
|
||||
// NOTE/TODO/XXX:
|
||||
// AnteHandlers must have their own defer/recover in order
|
||||
// for the BaseApp to know how much gas was used used!
|
||||
// This is because the GasMeter is created in the AnteHandler,
|
||||
// but if it panics the context won't be set properly in runTx's recover ...
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
@@ -1031,3 +1057,162 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
func TestGasConsumptionBadTx(t *testing.T) {
|
||||
gasWanted := uint64(5)
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasWanted))
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
case sdk.ErrorOutOfGas:
|
||||
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
|
||||
res = sdk.ErrOutOfGas(log).Result()
|
||||
res.GasWanted = gasWanted
|
||||
res.GasUsed = newCtx.GasMeter().GasConsumed()
|
||||
default:
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
txTest := tx.(txTest)
|
||||
newCtx.GasMeter().ConsumeGas(uint64(txTest.Counter), "counter-ante")
|
||||
if txTest.FailOnAnte {
|
||||
return newCtx, sdk.ErrInternal("ante handler failure").Result(), true
|
||||
}
|
||||
|
||||
res = sdk.Result{
|
||||
GasWanted: gasWanted,
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
count := msg.(msgCounter).Counter
|
||||
ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
|
||||
return sdk.Result{}
|
||||
})
|
||||
}
|
||||
|
||||
cdc := codec.New()
|
||||
registerTestCodec(cdc)
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
app.InitChain(abci.RequestInitChain{
|
||||
ConsensusParams: &abci.ConsensusParams{
|
||||
BlockSize: &abci.BlockSizeParams{
|
||||
MaxGas: 9,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
|
||||
tx := newTxCounter(5, 0)
|
||||
tx.setFailOnAnte(true)
|
||||
txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := app.DeliverTx(txBytes)
|
||||
require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
// require next tx to fail due to black gas limit
|
||||
tx = newTxCounter(5, 0)
|
||||
txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res = app.DeliverTx(txBytes)
|
||||
require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
}
|
||||
|
||||
// Test that we can only query from the latest committed state.
|
||||
func TestQuery(t *testing.T) {
|
||||
key, value := []byte("hello"), []byte("goodbye")
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// NOTE: "/store/key1" tells us KVStore
|
||||
// and the final "/key" says to use the data as the
|
||||
// key in the given KVStore ...
|
||||
query := abci.RequestQuery{
|
||||
Path: "/store/key1/key",
|
||||
Data: key,
|
||||
}
|
||||
tx := newTxCounter(0, 0)
|
||||
|
||||
// query is empty before we do anything
|
||||
res := app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a CheckTx
|
||||
resTx := app.Check(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a DeliverTx before we commit
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
resTx = app.Deliver(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query returns correct value after Commit
|
||||
app.Commit()
|
||||
res = app.Query(query)
|
||||
require.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
// Test p2p filter queries
|
||||
func TestP2PQuery(t *testing.T) {
|
||||
addrPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
|
||||
require.Equal(t, "1.1.1.1:8000", addrport)
|
||||
return abci.ResponseQuery{Code: uint32(3)}
|
||||
})
|
||||
}
|
||||
|
||||
pubkeyPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
|
||||
require.Equal(t, "testpubkey", pubkey)
|
||||
return abci.ResponseQuery{Code: uint32(4)}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+3
-28
@@ -1,13 +1,13 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/abci/server"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"regexp"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString
|
||||
|
||||
// nolint - Mostly for testing
|
||||
func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(runTxModeCheck, nil, tx)
|
||||
@@ -22,28 +22,3 @@ func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(runTxModeDeliver, nil, tx)
|
||||
}
|
||||
|
||||
// RunForever BasecoinApp execution and cleanup
|
||||
func RunForever(app abci.Application) {
|
||||
|
||||
// Start the ABCI server
|
||||
srv, err := server.NewServer("0.0.0.0:26658", "socket", app)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
return
|
||||
}
|
||||
err = srv.Start()
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
err := srv.Stop()
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+1
-23
@@ -18,7 +18,7 @@ func SetPruning(opts sdk.PruningOptions) func(*BaseApp) {
|
||||
return func(bap *BaseApp) { bap.cms.SetPruning(opts) }
|
||||
}
|
||||
|
||||
// SetMinimumGasPrices returns an option that sets the minimum gas prices on the app.
|
||||
// SetMinGasPrices returns an option that sets the minimum gas prices on the app.
|
||||
func SetMinGasPrices(gasPricesStr string) func(*BaseApp) {
|
||||
gasPrices, err := sdk.ParseDecCoins(gasPricesStr)
|
||||
if err != nil {
|
||||
@@ -97,25 +97,3 @@ func (app *BaseApp) SetFauxMerkleMode() {
|
||||
}
|
||||
app.fauxMerkleMode = true
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// TODO: move these out of this file?
|
||||
|
||||
func (app *BaseApp) Router() Router {
|
||||
if app.sealed {
|
||||
panic("Router() on sealed BaseApp")
|
||||
}
|
||||
return app.router
|
||||
}
|
||||
|
||||
func (app *BaseApp) QueryRouter() QueryRouter {
|
||||
return app.queryRouter
|
||||
}
|
||||
|
||||
func (app *BaseApp) Seal() { app.sealed = true }
|
||||
func (app *BaseApp) IsSealed() bool { return app.sealed }
|
||||
func (app *BaseApp) enforceSeal() {
|
||||
if !app.sealed {
|
||||
panic("enforceSeal() on BaseApp but not sealed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package baseapp
|
||||
|
||||
/*
|
||||
XXX Make this work with MultiStore.
|
||||
XXX It will require some interfaces updates in store/types.go.
|
||||
|
||||
if len(reqQuery.Data) == 0 {
|
||||
resQuery.Log = "Query cannot be zero length"
|
||||
resQuery.Code = abci.CodeType_EncodingError
|
||||
return
|
||||
}
|
||||
|
||||
// set the query response height to current
|
||||
tree := app.state.Committed()
|
||||
|
||||
height := reqQuery.Height
|
||||
if height == 0 {
|
||||
// TODO: once the rpc actually passes in non-zero
|
||||
// heights we can use to query right after a tx
|
||||
// we must retrun most recent, even if apphash
|
||||
// is not yet in the blockchain
|
||||
|
||||
withProof := app.CommittedHeight() - 1
|
||||
if tree.Tree.VersionExists(withProof) {
|
||||
height = withProof
|
||||
} else {
|
||||
height = app.CommittedHeight()
|
||||
}
|
||||
}
|
||||
resQuery.Height = height
|
||||
|
||||
switch reqQuery.Path {
|
||||
case "/store", "/key": // Get by key
|
||||
key := reqQuery.Data // Data holds the key bytes
|
||||
resQuery.Key = key
|
||||
if reqQuery.Prove {
|
||||
value, proof, err := tree.GetVersionedWithProof(key, height)
|
||||
if err != nil {
|
||||
resQuery.Log = err.Error()
|
||||
break
|
||||
}
|
||||
resQuery.Value = value
|
||||
resQuery.Proof = proof.Bytes()
|
||||
} else {
|
||||
value := tree.Get(key)
|
||||
resQuery.Value = value
|
||||
}
|
||||
|
||||
default:
|
||||
resQuery.Code = abci.CodeType_UnknownRequest
|
||||
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
|
||||
}
|
||||
return
|
||||
*/
|
||||
@@ -1,97 +0,0 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Test that we can only query from the latest committed state.
|
||||
func TestQuery(t *testing.T) {
|
||||
key, value := []byte("hello"), []byte("goodbye")
|
||||
anteOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
|
||||
app.InitChain(abci.RequestInitChain{})
|
||||
|
||||
// NOTE: "/store/key1" tells us KVStore
|
||||
// and the final "/key" says to use the data as the
|
||||
// key in the given KVStore ...
|
||||
query := abci.RequestQuery{
|
||||
Path: "/store/key1/key",
|
||||
Data: key,
|
||||
}
|
||||
tx := newTxCounter(0, 0)
|
||||
|
||||
// query is empty before we do anything
|
||||
res := app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a CheckTx
|
||||
resTx := app.Check(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a DeliverTx before we commit
|
||||
app.BeginBlock(abci.RequestBeginBlock{})
|
||||
resTx = app.Deliver(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query returns correct value after Commit
|
||||
app.Commit()
|
||||
res = app.Query(query)
|
||||
require.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
// Test p2p filter queries
|
||||
func TestP2PQuery(t *testing.T) {
|
||||
addrPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
|
||||
require.Equal(t, "1.1.1.1:8000", addrport)
|
||||
return abci.ResponseQuery{Code: uint32(3)}
|
||||
})
|
||||
}
|
||||
|
||||
pubkeyPeerFilterOpt := func(bapp *BaseApp) {
|
||||
bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
|
||||
require.Equal(t, "testpubkey", pubkey)
|
||||
return abci.ResponseQuery{Code: uint32(4)}
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt)
|
||||
|
||||
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)
|
||||
}
|
||||
+20
-16
@@ -1,6 +1,8 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
@@ -10,32 +12,34 @@ type QueryRouter interface {
|
||||
Route(path string) (h sdk.Querier)
|
||||
}
|
||||
|
||||
type queryrouter struct {
|
||||
type queryRouter struct {
|
||||
routes map[string]sdk.Querier
|
||||
}
|
||||
|
||||
// nolint
|
||||
// NewRouter - create new router
|
||||
// TODO either make Function unexported or make return type (router) Exported
|
||||
func NewQueryRouter() *queryrouter {
|
||||
return &queryrouter{
|
||||
// NewQueryRouter returns a reference to a new queryRouter.
|
||||
//
|
||||
// TODO: Either make the function private or make return type (queryRouter) public.
|
||||
func NewQueryRouter() *queryRouter { // nolint: golint
|
||||
return &queryRouter{
|
||||
routes: map[string]sdk.Querier{},
|
||||
}
|
||||
}
|
||||
|
||||
// AddRoute - Adds an sdk.Querier to the route provided. Panics on duplicate
|
||||
func (rtr *queryrouter) AddRoute(r string, q sdk.Querier) QueryRouter {
|
||||
if !isAlphaNumeric(r) {
|
||||
// AddRoute adds a query path to the router with a given Querier. It will panic
|
||||
// if a duplicate route is given. The route must be alphanumeric.
|
||||
func (qrt *queryRouter) AddRoute(path string, q sdk.Querier) QueryRouter {
|
||||
if !isAlphaNumeric(path) {
|
||||
panic("route expressions can only contain alphanumeric characters")
|
||||
}
|
||||
if rtr.routes[r] != nil {
|
||||
panic("route has already been initialized")
|
||||
if qrt.routes[path] != nil {
|
||||
panic(fmt.Sprintf("route %s has already been initialized", path))
|
||||
}
|
||||
rtr.routes[r] = q
|
||||
return rtr
|
||||
|
||||
qrt.routes[path] = q
|
||||
return qrt
|
||||
}
|
||||
|
||||
// Returns the sdk.Querier for a certain route path
|
||||
func (rtr *queryrouter) Route(path string) (h sdk.Querier) {
|
||||
return rtr.routes[path]
|
||||
// Route returns the Querier for a given query route path.
|
||||
func (qrt *queryRouter) Route(path string) sdk.Querier {
|
||||
return qrt.routes[path]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var testQuerier = func(_ sdk.Context, _ []string, _ abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestQueryRouter(t *testing.T) {
|
||||
qr := NewQueryRouter()
|
||||
|
||||
// require panic on invalid route
|
||||
require.Panics(t, func() {
|
||||
qr.AddRoute("*", testQuerier)
|
||||
})
|
||||
|
||||
qr.AddRoute("testRoute", testQuerier)
|
||||
q := qr.Route("testRoute")
|
||||
require.NotNil(t, q)
|
||||
|
||||
// require panic on duplicate route
|
||||
require.Panics(t, func() {
|
||||
qr.AddRoute("testRoute", testQuerier)
|
||||
})
|
||||
}
|
||||
+20
-28
@@ -1,7 +1,7 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
@@ -12,44 +12,36 @@ type Router interface {
|
||||
Route(path string) (h sdk.Handler)
|
||||
}
|
||||
|
||||
// map a transaction type to a handler and an initgenesis function
|
||||
type route struct {
|
||||
r string
|
||||
h sdk.Handler
|
||||
}
|
||||
|
||||
type router struct {
|
||||
routes []route
|
||||
routes map[string]sdk.Handler
|
||||
}
|
||||
|
||||
// nolint
|
||||
// NewRouter - create new router
|
||||
// TODO either make Function unexported or make return type (router) Exported
|
||||
func NewRouter() *router {
|
||||
// NewRouter returns a reference to a new router.
|
||||
//
|
||||
// TODO: Either make the function private or make return type (router) public.
|
||||
func NewRouter() *router { // nolint: golint
|
||||
return &router{
|
||||
routes: make([]route, 0),
|
||||
routes: make(map[string]sdk.Handler),
|
||||
}
|
||||
}
|
||||
|
||||
var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString
|
||||
|
||||
// AddRoute - TODO add description
|
||||
func (rtr *router) AddRoute(r string, h sdk.Handler) Router {
|
||||
if !isAlphaNumeric(r) {
|
||||
// AddRoute adds a route path to the router with a given handler. The route must
|
||||
// be alphanumeric.
|
||||
func (rtr *router) AddRoute(path string, h sdk.Handler) Router {
|
||||
if !isAlphaNumeric(path) {
|
||||
panic("route expressions can only contain alphanumeric characters")
|
||||
}
|
||||
rtr.routes = append(rtr.routes, route{r, h})
|
||||
if rtr.routes[path] != nil {
|
||||
panic(fmt.Sprintf("route %s has already been initialized", path))
|
||||
}
|
||||
|
||||
rtr.routes[path] = h
|
||||
return rtr
|
||||
}
|
||||
|
||||
// Route - TODO add description
|
||||
// TODO handle expressive matches.
|
||||
func (rtr *router) Route(path string) (h sdk.Handler) {
|
||||
for _, route := range rtr.routes {
|
||||
if route.r == path {
|
||||
return route.h
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// Route returns a handler for a given route path.
|
||||
//
|
||||
// TODO: Handle expressive matches.
|
||||
func (rtr *router) Route(path string) sdk.Handler {
|
||||
return rtr.routes[path]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package baseapp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var testHandler = func(_ sdk.Context, _ sdk.Msg) sdk.Result {
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
func TestRouter(t *testing.T) {
|
||||
rtr := NewRouter()
|
||||
|
||||
// require panic on invalid route
|
||||
require.Panics(t, func() {
|
||||
rtr.AddRoute("*", testHandler)
|
||||
})
|
||||
|
||||
rtr.AddRoute("testRoute", testHandler)
|
||||
h := rtr.Route("testRoute")
|
||||
require.NotNil(t, h)
|
||||
|
||||
// require panic on duplicate route
|
||||
require.Panics(t, func() {
|
||||
rtr.AddRoute("testRoute", testHandler)
|
||||
})
|
||||
}
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"chain_id": "foo_bar_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "6880db93598e283a67c4d88fc67a8858aa2de70f713fe94a5109e29c137100c2"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "blank",
|
||||
"amount": 12345
|
||||
},
|
||||
{
|
||||
"denom": "ETH",
|
||||
"amount": 654321
|
||||
}
|
||||
]
|
||||
}],
|
||||
"plugin_options": ["plugin1/key1", "value1", "plugin1/key2", "value2"]
|
||||
}
|
||||
}
|
||||
Vendored
-39
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"chain_id": "addr_accounts_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"name": "alice",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "one",
|
||||
"amount": 111
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "bob",
|
||||
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "two",
|
||||
"amount": 222
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "sam",
|
||||
"pub_key": {
|
||||
"type": "secp256k1",
|
||||
"data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "four",
|
||||
"amount": 444
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
Vendored
-52
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"chain_id": "addr_accounts_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"name": "alice",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "one",
|
||||
"amount": 111
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "bob",
|
||||
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "two",
|
||||
"amount": 222
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "carl",
|
||||
"address": "1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "177C0AC45E86257F0708DC085D592AB22AAEECD1D26381B757F7C96135921858"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "three",
|
||||
"amount": 333
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "sam",
|
||||
"pub_key": {
|
||||
"type": "secp256k1",
|
||||
"data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "four",
|
||||
"amount": 444
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"chain_id": "foo_bar_chain"
|
||||
}
|
||||
Reference in New Issue
Block a user