Merge PR #3468: Increase BaseApp Test Coverage

This commit is contained in:
Alexander Bezobchuk
2019-02-06 04:11:57 +01:00
committed by Christopher Goes
parent 1766a73a4c
commit 52350c592b
15 changed files with 497 additions and 526 deletions
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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")
}
}
-54
View File
@@ -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
*/
-97
View File
@@ -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
View File
@@ -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]
}
+33
View File
@@ -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
View File
@@ -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]
}
+31
View File
@@ -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)
})
}
-22
View File
@@ -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"]
}
}
-39
View File
@@ -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
}
]
}]
}
}
-52
View File
@@ -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
}
]
}]
}
}
-3
View File
@@ -1,3 +0,0 @@
{
"chain_id": "foo_bar_chain"
}