package baseapp import ( "bytes" "encoding/binary" "encoding/json" "fmt" "os" "strings" "sync" "testing" "github.com/cosmos/cosmos-sdk/codec/testdata" "github.com/gogo/protobuf/jsonpb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/rootmulti" store "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var ( capKey1 = sdk.NewKVStoreKey("key1") capKey2 = sdk.NewKVStoreKey("key2") ) type paramStore struct { db *dbm.MemDB } func (ps *paramStore) Set(_ sdk.Context, key []byte, value interface{}) { bz, err := json.Marshal(value) if err != nil { panic(err) } ps.db.Set(key, bz) } func (ps *paramStore) Has(_ sdk.Context, key []byte) bool { ok, err := ps.db.Has(key) if err != nil { panic(err) } return ok } func (ps *paramStore) Get(_ sdk.Context, key []byte, ptr interface{}) { bz, err := ps.db.Get(key) if err != nil { panic(err) } if len(bz) == 0 { return } if err := json.Unmarshal(bz, ptr); err != nil { panic(err) } } func defaultLogger() log.Logger { return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") } func newBaseApp(name string, options ...func(*BaseApp)) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() codec := codec.New() registerTestCodec(codec) return NewBaseApp(name, logger, db, testTxDecoder(codec), options...) } func registerTestCodec(cdc *codec.Codec) { // register Tx, Msg sdk.RegisterCodec(cdc) // register test types cdc.RegisterConcrete(&txTest{}, "cosmos-sdk/baseapp/txTest", nil) cdc.RegisterConcrete(&msgCounter{}, "cosmos-sdk/baseapp/msgCounter", nil) cdc.RegisterConcrete(&msgCounter2{}, "cosmos-sdk/baseapp/msgCounter2", nil) cdc.RegisterConcrete(&msgNoRoute{}, "cosmos-sdk/baseapp/msgNoRoute", nil) } // simple one store baseapp func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) app.MountStores(capKey1, capKey2) app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) // stores are mounted err := app.LoadLatestVersion() require.Nil(t, err) return app } func TestMountStores(t *testing.T) { app := setupBaseApp(t) // check both stores store1 := app.cms.GetCommitKVStore(capKey1) require.NotNil(t, store1) store2 := app.cms.GetCommitKVStore(capKey2) require.NotNil(t, store2) } // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { logger := defaultLogger() pruningOpt := SetPruning(store.PruneNothing) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) // make a cap key and mount the store err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) emptyCommitID := sdk.CommitID{} // fresh store has zero/empty last commit lastHeight := app.LastBlockHeight() lastID := app.LastCommitID() require.Equal(t, int64(0), lastHeight) require.Equal(t, emptyCommitID, lastID) // execute a block, collect commit ID header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res := app.Commit() commitID1 := sdk.CommitID{Version: 1, Hash: res.Data} // execute a block, collect commit ID header = abci.Header{Height: 2} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() commitID2 := sdk.CommitID{Version: 2, Hash: res.Data} // reload with LoadLatestVersion app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores() err = app.LoadLatestVersion() require.Nil(t, err) testLoadVersionHelper(t, app, int64(2), commitID2) // reload with LoadVersion, see if you can commit the same block and get // the same result app = NewBaseApp(name, logger, db, nil, pruningOpt) err = app.LoadVersion(1) require.Nil(t, err) testLoadVersionHelper(t, app, int64(1), commitID1) app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.Commit() testLoadVersionHelper(t, app, int64(2), commitID2) } func useDefaultLoader(app *BaseApp) { app.SetStoreLoader(DefaultStoreLoader) } func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { rs := rootmulti.NewStore(db) rs.SetPruning(store.PruneNothing) key := sdk.NewKVStoreKey(storeKey) rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) err := rs.LoadLatestVersion() require.Nil(t, err) require.Equal(t, int64(0), rs.LastCommitID().Version) // write some data in substore kv, _ := rs.GetStore(key).(store.KVStore) require.NotNil(t, kv) kv.Set(k, v) commitID := rs.Commit() require.Equal(t, int64(1), commitID.Version) } func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { rs := rootmulti.NewStore(db) rs.SetPruning(store.PruneSyncable) key := sdk.NewKVStoreKey(storeKey) rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) err := rs.LoadLatestVersion() require.Nil(t, err) require.Equal(t, ver, rs.LastCommitID().Version) // query data in substore kv, _ := rs.GetStore(key).(store.KVStore) require.NotNil(t, kv) require.Equal(t, v, kv.Get(k)) } // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestSetLoader(t *testing.T) { cases := map[string]struct { setLoader func(*BaseApp) origStoreKey string loadStoreKey string }{ "don't set loader": { origStoreKey: "foo", loadStoreKey: "foo", }, "default loader": { setLoader: useDefaultLoader, origStoreKey: "foo", loadStoreKey: "foo", }, } k := []byte("key") v := []byte("value") for name, tc := range cases { tc := tc t.Run(name, func(t *testing.T) { // prepare a db with some data db := dbm.NewMemDB() initStore(t, db, tc.origStoreKey, k, v) // load the app with the existing db opts := []func(*BaseApp){SetPruning(store.PruneNothing)} if tc.setLoader != nil { opts = append(opts, tc.setLoader) } app := NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) err := app.LoadLatestVersion() require.Nil(t, err) // "execute" one block app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: 2}}) res := app.Commit() require.NotNil(t, res.Data) // check db is properly updated checkStore(t, db, 2, tc.loadStoreKey, k, v) checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil) }) } } func TestAppVersionSetterGetter(t *testing.T) { logger := defaultLogger() pruningOpt := SetPruning(store.PruneSyncable) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) require.Equal(t, "", app.AppVersion()) res := app.Query(abci.RequestQuery{Path: "app/version"}) require.True(t, res.IsOK()) require.Equal(t, "", string(res.Value)) versionString := "1.0.0" app.SetAppVersion(versionString) require.Equal(t, versionString, app.AppVersion()) res = app.Query(abci.RequestQuery{Path: "app/version"}) require.True(t, res.IsOK()) require.Equal(t, versionString, string(res.Value)) } func TestLoadVersionInvalid(t *testing.T) { logger := log.NewNopLogger() pruningOpt := SetPruning(store.PruneNothing) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) err := app.LoadLatestVersion() require.Nil(t, err) // require error when loading an invalid version err = app.LoadVersion(-1) require.Error(t, err) header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res := app.Commit() commitID1 := sdk.CommitID{Version: 1, Hash: res.Data} // create a new app with the stores mounted under the same cap key app = NewBaseApp(name, logger, db, nil, pruningOpt) // require we can load the latest version err = app.LoadVersion(1) require.Nil(t, err) testLoadVersionHelper(t, app, int64(1), commitID1) // require error when loading an invalid version err = app.LoadVersion(2) require.Error(t, err) } func TestLoadVersionPruning(t *testing.T) { logger := log.NewNopLogger() pruningOptions := store.PruningOptions{ KeepEvery: 2, SnapshotEvery: 6, } pruningOpt := SetPruning(pruningOptions) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("key1") app.MountStores(capKey) err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) emptyCommitID := sdk.CommitID{} // fresh store has zero/empty last commit lastHeight := app.LastBlockHeight() lastID := app.LastCommitID() require.Equal(t, int64(0), lastHeight) require.Equal(t, emptyCommitID, lastID) // execute a block header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res := app.Commit() // execute a block, collect commit ID header = abci.Header{Height: 2} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() commitID2 := sdk.CommitID{Version: 2, Hash: res.Data} // execute a block header = abci.Header{Height: 3} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() commitID3 := sdk.CommitID{Version: 3, Hash: res.Data} // reload with LoadLatestVersion, check it loads last flushed version app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores(capKey) err = app.LoadLatestVersion() require.Nil(t, err) testLoadVersionHelper(t, app, int64(2), commitID2) // re-execute block 3 and check it is same CommitID header = abci.Header{Height: 3} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() recommitID3 := sdk.CommitID{Version: 3, Hash: res.Data} require.Equal(t, commitID3, recommitID3, "Commits of identical blocks not equal after reload") // execute a block, collect commit ID header = abci.Header{Height: 4} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() commitID4 := sdk.CommitID{Version: 4, Hash: res.Data} // execute a block header = abci.Header{Height: 5} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() // reload with LoadLatestVersion, check it loads last flushed version app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores(capKey) err = app.LoadLatestVersion() require.Nil(t, err) testLoadVersionHelper(t, app, int64(4), commitID4) // reload with LoadVersion of previous flushed version // and check it fails since previous flush should be pruned app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores(capKey) err = app.LoadVersion(2) require.NotNil(t, err) } func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) { lastHeight := app.LastBlockHeight() lastID := app.LastCommitID() require.Equal(t, expectedHeight, lastHeight) require.Equal(t, expectedID, lastID) } func TestOptionFunction(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() bap := NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name")) require.Equal(t, bap.name, "new name", "BaseApp should have had name changed via option function") } func testChangeNameHelper(name string) func(*BaseApp) { return func(bap *BaseApp) { bap.name = name } } // Test that txs can be unmarshalled and read and that // correct error codes are returned when not func TestTxDecoder(t *testing.T) { codec := codec.New() registerTestCodec(codec) app := newBaseApp(t.Name()) tx := newTxCounter(1, 0) txBytes := codec.MustMarshalBinaryBare(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. func TestInfo(t *testing.T) { app := newBaseApp(t.Name()) // ----- test an empty response ------- reqInfo := abci.RequestInfo{} res := app.Info(reqInfo) // should be empty assert.Equal(t, "", res.Version) assert.Equal(t, t.Name(), res.GetData()) assert.Equal(t, int64(0), res.LastBlockHeight) require.Equal(t, []uint8(nil), res.LastBlockAppHash) // ----- test a proper response ------- // TODO } func TestBaseAppOptionSeal(t *testing.T) { app := setupBaseApp(t) require.Panics(t, func() { app.SetName("") }) require.Panics(t, func() { app.SetAppVersion("") }) 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.SetIDPeerFilter(nil) }) require.Panics(t, func() { app.SetFauxMerkleMode() }) require.Panics(t, func() { app.SetRouter(NewRouter()) }) } func TestSetMinGasPrices(t *testing.T) { minGasPrices := sdk.DecCoins{sdk.NewInt64DecCoin("stake", 5000)} app := newBaseApp(t.Name(), SetMinGasPrices(minGasPrices.String())) require.Equal(t, minGasPrices, app.minGasPrices) } func TestInitChainer(t *testing.T) { name := t.Name() // keep the db and logger ourselves so // we can reload the same app later db := dbm.NewMemDB() logger := defaultLogger() app := NewBaseApp(name, logger, db, nil) capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") app.MountStores(capKey, capKey2) // set a value in the store on init chain key, value := []byte("hello"), []byte("goodbye") var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { store := ctx.KVStore(capKey) store.Set(key, value) return abci.ResponseInitChain{} } query := abci.RequestQuery{ Path: "/store/main/key", Data: key, } // initChainer is nil - nothing happens app.InitChain(abci.RequestInitChain{}) res := app.Query(query) require.Equal(t, 0, len(res.Value)) // set initChainer and try again - should see the value app.SetInitChainer(initChainer) // stores are mounted and private members are set - sealing baseapp err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) require.Equal(t, int64(0), app.LastBlockHeight()) app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty // assert that chainID is set correctly in InitChain chainID := app.deliverState.ctx.ChainID() require.Equal(t, "test-chain-id", chainID, "ChainID in deliverState not set correctly in InitChain") chainID = app.checkState.ctx.ChainID() require.Equal(t, "test-chain-id", chainID, "ChainID in checkState not set correctly in InitChain") app.Commit() res = app.Query(query) require.Equal(t, int64(1), app.LastBlockHeight()) require.Equal(t, value, res.Value) // reload app app = NewBaseApp(name, logger, db, nil) app.SetInitChainer(initChainer) app.MountStores(capKey, capKey2) err = app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) require.Equal(t, int64(1), app.LastBlockHeight()) // ensure we can still query after reloading res = app.Query(query) require.Equal(t, value, res.Value) // commit and ensure we can still query header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.Commit() res = app.Query(query) require.Equal(t, value, res.Value) } // Simple tx with a list of Msgs. type txTest struct { Msgs []sdk.Msg Counter int64 FailOnAnte bool } func (tx *txTest) setFailOnAnte(fail bool) { tx.FailOnAnte = fail } func (tx *txTest) setFailOnHandler(fail bool) { for i, msg := range tx.Msgs { tx.Msgs[i] = msgCounter{msg.(msgCounter).Counter, fail} } } // Implements Tx func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs } func (tx txTest) ValidateBasic() error { return nil } const ( routeMsgCounter = "msgCounter" routeMsgCounter2 = "msgCounter2" ) // ValidateBasic() fails on negative counters. // Otherwise it's up to the handlers type msgCounter struct { Counter int64 FailOnHandler bool } // dummy implementation of proto.Message func (msg msgCounter) Reset() {} func (msg msgCounter) String() string { return "TODO" } func (msg msgCounter) ProtoMessage() {} // Implements Msg func (msg msgCounter) Route() string { return routeMsgCounter } func (msg msgCounter) Type() string { return "counter1" } func (msg msgCounter) GetSignBytes() []byte { return nil } func (msg msgCounter) GetSigners() []sdk.AccAddress { return nil } func (msg msgCounter) ValidateBasic() error { if msg.Counter >= 0 { return nil } return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counter should be a non-negative integer") } func newTxCounter(counter int64, msgCounters ...int64) *txTest { msgs := make([]sdk.Msg, 0, len(msgCounters)) for _, c := range msgCounters { msgs = append(msgs, msgCounter{c, false}) } return &txTest{msgs, counter, false} } // a msg we dont know how to route type msgNoRoute struct { msgCounter } func (tx msgNoRoute) Route() string { return "noroute" } // a msg we dont know how to decode type msgNoDecode struct { msgCounter } func (tx msgNoDecode) Route() string { return routeMsgCounter } // Another counter msg. Duplicate of msgCounter type msgCounter2 struct { Counter int64 } // dummy implementation of proto.Message func (msg msgCounter2) Reset() {} func (msg msgCounter2) String() string { return "TODO" } func (msg msgCounter2) ProtoMessage() {} // Implements Msg func (msg msgCounter2) Route() string { return routeMsgCounter2 } func (msg msgCounter2) Type() string { return "counter2" } func (msg msgCounter2) GetSignBytes() []byte { return nil } func (msg msgCounter2) GetSigners() []sdk.AccAddress { return nil } func (msg msgCounter2) ValidateBasic() error { if msg.Counter >= 0 { return nil } return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counter should be a non-negative integer") } // amino decode func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, error) { var tx txTest if len(txBytes) == 0 { return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty") } err := cdc.UnmarshalBinaryBare(txBytes, &tx) if err != nil { return nil, sdkerrors.ErrTxDecode } return tx, nil } } func anteHandlerTxTest(t *testing.T, capKey sdk.StoreKey, storeKey []byte) sdk.AnteHandler { return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { store := ctx.KVStore(capKey) txTest := tx.(txTest) if txTest.FailOnAnte { return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") } _, err := incrementingCounter(t, store, storeKey, txTest.Counter) if err != nil { return ctx, err } ctx.EventManager().EmitEvents( counterEvent("ante_handler", txTest.Counter), ) return ctx, nil } } func counterEvent(evType string, msgCount int64) sdk.Events { return sdk.Events{ sdk.NewEvent( evType, sdk.NewAttribute("update_counter", fmt.Sprintf("%d", msgCount)), ), } } func handlerMsgCounter(t *testing.T, capKey sdk.StoreKey, deliverKey []byte) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) store := ctx.KVStore(capKey) var msgCount int64 switch m := msg.(type) { case *msgCounter: if m.FailOnHandler { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "message handler failure") } msgCount = m.Counter case *msgCounter2: msgCount = m.Counter } ctx.EventManager().EmitEvents( counterEvent(sdk.EventTypeMessage, msgCount), ) res, err := incrementingCounter(t, store, deliverKey, msgCount) if err != nil { return nil, err } res.Events = ctx.EventManager().Events().ToABCIEvents() return res, nil } } func getIntFromStore(store sdk.KVStore, key []byte) int64 { bz := store.Get(key) if len(bz) == 0 { return 0 } i, err := binary.ReadVarint(bytes.NewBuffer(bz)) if err != nil { panic(err) } return i } func setIntOnStore(store sdk.KVStore, key []byte, i int64) { bz := make([]byte, 8) n := binary.PutVarint(bz, i) store.Set(key, bz[:n]) } // check counter matches what's in store. // increment and store func incrementingCounter(t *testing.T, store sdk.KVStore, counterKey []byte, counter int64) (*sdk.Result, error) { storedCounter := getIntFromStore(store, counterKey) require.Equal(t, storedCounter, counter) setIntOnStore(store, counterKey, counter+1) return &sdk.Result{}, nil } //--------------------------------------------------------------------- // Tx processing - CheckTx, DeliverTx, SimulateTx. // These tests use the serialized tx as input, while most others will use the // Check(), Deliver(), Simulate() methods directly. // Ensure that Check/Deliver/Simulate work as expected with the store. // Test that successive CheckTx can see each others' effects // on the store within a block, and that the CheckTx state // gets reset to the latest committed state during Commit func TestCheckTx(t *testing.T) { // This ante handler reads the key and checks that the value matches the current counter. // This ensures changes to the kvstore persist across successive CheckTx. counterKey := []byte("counter-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, counterKey)) } routerOpt := func(bapp *BaseApp) { // TODO: can remove this once CheckTx doesnt process msgs. bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) nTxs := int64(5) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder codec := codec.New() registerTestCodec(codec) for i := int64(0); i < nTxs; i++ { tx := newTxCounter(i, 0) // no messages txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) r := app.CheckTx(abci.RequestCheckTx{Tx: txBytes}) require.Empty(t, r.GetEvents()) require.True(t, r.IsOK(), fmt.Sprintf("%v", r)) } checkStateStore := app.checkState.ctx.KVStore(capKey1) storedCounter := getIntFromStore(checkStateStore, counterKey) // Ensure AnteHandler ran require.Equal(t, nTxs, storedCounter) // If a block is committed, CheckTx state should be reset. header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.EndBlock(abci.RequestEndBlock{}) app.Commit() checkStateStore = app.checkState.ctx.KVStore(capKey1) storedBytes := checkStateStore.Get(counterKey) require.Nil(t, storedBytes) } // Test that successive DeliverTx can see each others' effects // on the store, both within and across blocks. func TestDeliverTx(t *testing.T) { // test increments in the ante anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } // test increments in the handler deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder codec := codec.New() registerTestCodec(codec) nBlocks := 3 txPerHeight := 5 for blockN := 0; blockN < nBlocks; blockN++ { header := abci.Header{Height: int64(blockN) + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) events := res.GetEvents() require.Len(t, events, 3, "should contain ante handler, message type and counter events respectively") require.Equal(t, counterEvent("ante_handler", counter).ToABCIEvents()[0], events[0], "ante handler event") require.Equal(t, counterEvent(sdk.EventTypeMessage, counter).ToABCIEvents()[0], events[2], "msg handler update counter event") } app.EndBlock(abci.RequestEndBlock{}) app.Commit() } } // Number of messages doesn't matter to CheckTx. func TestMultiMsgCheckTx(t *testing.T) { // TODO: ensure we get the same results // with one message or many } // One call to DeliverTx should process all the messages, in order. func TestMultiMsgDeliverTx(t *testing.T) { // increment the tx counter anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } // increment the msg counter deliverKey := []byte("deliver-key") deliverKey2 := []byte("deliver-key2") routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) bapp.Router().AddRoute(routeMsgCounter2, handlerMsgCounter(t, capKey1, deliverKey2)) } app := setupBaseApp(t, anteOpt, routerOpt) // Create same codec used in txDecoder codec := codec.New() registerTestCodec(codec) // run a multi-msg tx // with all msgs the same route header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(0, 0, 1, 2) txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) store := app.deliverState.ctx.KVStore(capKey1) // tx counter only incremented once txCounter := getIntFromStore(store, anteKey) require.Equal(t, int64(1), txCounter) // msg counter incremented three times msgCounter := getIntFromStore(store, deliverKey) require.Equal(t, int64(3), msgCounter) // replace the second message with a msgCounter2 tx = newTxCounter(1, 3) tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{1}) txBytes, err = codec.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) store = app.deliverState.ctx.KVStore(capKey1) // tx counter only incremented once txCounter = getIntFromStore(store, anteKey) require.Equal(t, int64(2), txCounter) // original counter increments by one // new counter increments by two msgCounter = getIntFromStore(store, deliverKey) require.Equal(t, int64(4), msgCounter) msgCounter2 := getIntFromStore(store, deliverKey2) require.Equal(t, int64(2), msgCounter2) } // Interleave calls to Check and Deliver and ensure // that there is no cross-talk. Check sees results of the previous Check calls // and Deliver sees that of the previous Deliver calls, but they don't see eachother. func TestConcurrentCheckDeliver(t *testing.T) { // TODO } // Simulate a transaction that uses gas to compute the gas. // Simulate() and Query("/app/simulate", txBytes) should give // the same results. func TestSimulateTx(t *testing.T) { gasConsumed := uint64(5) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasConsumed)) return }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx.GasMeter().ConsumeGas(gasConsumed, "test") return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder cdc := codec.New() registerTestCodec(cdc) nBlocks := 3 for blockN := 0; blockN < nBlocks; blockN++ { count := int64(blockN + 1) header := abci.Header{Height: count} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(count, count) txBytes, err := cdc.MarshalBinaryBare(tx) require.Nil(t, err) // simulate a message, check gas reported gInfo, result, err := app.Simulate(txBytes, tx) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) // simulate again, same result gInfo, result, err = app.Simulate(txBytes, tx) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) // simulate by calling Query with encoded tx query := abci.RequestQuery{ Path: "/app/simulate", Data: txBytes, } queryResult := app.Query(query) require.True(t, queryResult.IsOK(), queryResult.Log) var simRes sdk.SimulationResponse require.NoError(t, jsonpb.Unmarshal(strings.NewReader(string(queryResult.Value)), &simRes)) require.Equal(t, gInfo, simRes.GasInfo) require.Equal(t, result.Log, simRes.Result.Log) require.Equal(t, result.Events, simRes.Result.Events) require.True(t, bytes.Equal(result.Data, simRes.Result.Data)) app.EndBlock(abci.RequestEndBlock{}) app.Commit() } } func TestRunInvalidTransaction(t *testing.T) { anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // transaction with no messages { emptyTx := &txTest{} _, result, err := app.Deliver(emptyTx) require.Error(t, err) require.Nil(t, result) space, code, _ := sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrInvalidRequest.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrInvalidRequest.ABCICode(), code, err) } // transaction where ValidateBasic fails { testCases := []struct { tx *txTest fail bool }{ {newTxCounter(0, 0), false}, {newTxCounter(-1, 0), false}, {newTxCounter(100, 100), false}, {newTxCounter(100, 5, 4, 3, 2, 1), false}, {newTxCounter(0, -1), true}, {newTxCounter(0, 1, -2), true}, {newTxCounter(0, 1, 2, -10, 5), true}, } for _, testCase := range testCases { tx := testCase.tx _, result, err := app.Deliver(tx) if testCase.fail { require.Error(t, err) space, code, _ := sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrInvalidSequence.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrInvalidSequence.ABCICode(), code, err) } else { require.NotNil(t, result) } } } // transaction with no known route { unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false} _, result, err := app.Deliver(unknownRouteTx) require.Error(t, err) require.Nil(t, result) space, code, _ := sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err) unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false} _, result, err = app.Deliver(unknownRouteTx) require.Error(t, err) require.Nil(t, result) space, code, _ = sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err) } // Transaction with an unregistered message { tx := newTxCounter(0, 0) tx.Msgs = append(tx.Msgs, msgNoDecode{}) // new codec so we can encode the tx, but we shouldn't be able to decode newCdc := codec.New() registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) txBytes, err := newCdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.EqualValues(t, sdkerrors.ErrTxDecode.ABCICode(), res.Code) require.EqualValues(t, sdkerrors.ErrTxDecode.Codespace(), res.Codespace) } } // Test that transactions exceeding gas limits fail func TestTxGasLimits(t *testing.T) { gasGranted := uint64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) // AnteHandlers must have their own defer/recover in order for the BaseApp // to know how much gas was 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 call. defer func() { if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: err = sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "out of gas in location: %v", rType.Descriptor) default: panic(r) } } }() count := tx.(*txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return newCtx, nil }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { count := msg.(msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) testCases := []struct { tx *txTest gasUsed uint64 fail bool }{ {newTxCounter(0, 0), 0, false}, {newTxCounter(1, 1), 2, false}, {newTxCounter(9, 1), 10, false}, {newTxCounter(1, 9), 10, false}, {newTxCounter(10, 0), 10, false}, {newTxCounter(0, 10), 10, false}, {newTxCounter(0, 8, 2), 10, false}, {newTxCounter(0, 5, 1, 1, 1, 1, 1), 10, false}, {newTxCounter(0, 5, 1, 1, 1, 1), 9, false}, {newTxCounter(9, 2), 11, true}, {newTxCounter(2, 9), 11, true}, {newTxCounter(9, 1, 1), 11, true}, {newTxCounter(1, 8, 1, 1), 11, true}, {newTxCounter(11, 0), 11, true}, {newTxCounter(0, 11), 11, true}, {newTxCounter(0, 5, 11), 16, true}, } for i, tc := range testCases { tx := tc.tx gInfo, result, err := app.Deliver(tx) // check gas used and wanted require.Equal(t, tc.gasUsed, gInfo.GasUsed, fmt.Sprintf("tc #%d; gas: %v, result: %v, err: %s", i, gInfo, result, err)) // check for out of gas if !tc.fail { require.NotNil(t, result, fmt.Sprintf("%d: %v, %v", i, tc, err)) } else { require.Error(t, err) require.Nil(t, result) space, code, _ := sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrOutOfGas.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrOutOfGas.ABCICode(), code, err) } } } // Test that transactions exceeding gas limits fail func TestMaxBlockGasLimits(t *testing.T) { gasGranted := uint64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) defer func() { if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: err = sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "out of gas in location: %v", rType.Descriptor) default: panic(r) } } }() count := tx.(*txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { count := msg.(msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{ ConsensusParams: &abci.ConsensusParams{ Block: &abci.BlockParams{ MaxGas: 100, }, }, }) testCases := []struct { tx *txTest numDelivers int gasUsedPerDeliver uint64 fail bool failAfterDeliver int }{ {newTxCounter(0, 0), 0, 0, false, 0}, {newTxCounter(9, 1), 2, 10, false, 0}, {newTxCounter(10, 0), 3, 10, false, 0}, {newTxCounter(10, 0), 10, 10, false, 0}, {newTxCounter(2, 7), 11, 9, false, 0}, {newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass {newTxCounter(10, 0), 11, 10, true, 10}, {newTxCounter(10, 0), 15, 10, true, 10}, {newTxCounter(9, 0), 12, 9, true, 11}, // fly past the limit } for i, tc := range testCases { tx := tc.tx // reset the block gas header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute the transaction multiple times for j := 0; j < tc.numDelivers; j++ { _, result, err := app.Deliver(tx) ctx := app.getState(runTxModeDeliver).ctx // check for failed transactions if tc.fail && (j+1) > tc.failAfterDeliver { require.Error(t, err, fmt.Sprintf("tc #%d; result: %v, err: %s", i, result, err)) require.Nil(t, result, fmt.Sprintf("tc #%d; result: %v, err: %s", i, result, err)) space, code, _ := sdkerrors.ABCIInfo(err, false) require.EqualValues(t, sdkerrors.ErrOutOfGas.Codespace(), space, err) require.EqualValues(t, sdkerrors.ErrOutOfGas.ABCICode(), code, err) require.True(t, ctx.BlockGasMeter().IsOutOfGas()) } else { // check gas used and wanted blockGasUsed := ctx.BlockGasMeter().GasConsumed() expBlockGasUsed := tc.gasUsedPerDeliver * uint64(j+1) require.Equal( t, expBlockGasUsed, blockGasUsed, fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, result), ) require.NotNil(t, result, fmt.Sprintf("tc #%d; currDeliver: %d, result: %v, err: %s", i, j, result, err)) require.False(t, ctx.BlockGasMeter().IsPastLimit()) } } } } // Test custom panic handling within app.DeliverTx method func TestCustomRunTxPanicHandler(t *testing.T) { const customPanicMsg = "test panic" anteErr := sdkerrors.Register("fakeModule", 100500, "fakeError") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { panic(sdkerrors.Wrap(anteErr, "anteHandler")) return }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return &sdk.Result{}, nil }) } app := setupBaseApp(t, anteOpt, routerOpt) header := abci.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.AddRunTxRecoveryHandler(func(recoveryObj interface{}) error { err, ok := recoveryObj.(error) if !ok { return nil } if anteErr.Is(err) { panic(customPanicMsg) } else { return nil } }) // Transaction should panic with custom handler above { tx := newTxCounter(0, 0) require.PanicsWithValue(t, customPanicMsg, func() { app.Deliver(tx) }) } } func TestBaseAppAnteHandler(t *testing.T) { anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) } cdc := codec.New() app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) registerTestCodec(cdc) header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute a tx that will fail ante handler execution // // NOTE: State should not be mutated here. This will be implicitly checked by // the next txs ante handler execution (anteHandlerTxTest). tx := newTxCounter(0, 0) tx.setFailOnAnte(true) txBytes, err := cdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx := app.getState(runTxModeDeliver).ctx store := ctx.KVStore(capKey1) require.Equal(t, int64(0), getIntFromStore(store, anteKey)) // execute at tx that will pass the ante handler (the checkTx state should // mutate) but will fail the message handler tx = newTxCounter(0, 0) tx.setFailOnHandler(true) txBytes, err = cdc.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx store = ctx.KVStore(capKey1) require.Equal(t, int64(1), getIntFromStore(store, anteKey)) require.Equal(t, int64(0), getIntFromStore(store, deliverKey)) // execute a successful ante handler and message execution where state is // implicitly checked by previous tx executions tx = newTxCounter(1, 0) txBytes, err = cdc.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.NotEmpty(t, res.Events) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx store = ctx.KVStore(capKey1) require.Equal(t, int64(2), getIntFromStore(store, anteKey)) require.Equal(t, int64(1), getIntFromStore(store, deliverKey)) // commit 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, err error) { 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) err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log) default: panic(r) } } }() txTest := tx.(txTest) newCtx.GasMeter().ConsumeGas(uint64(txTest.Counter), "counter-ante") if txTest.FailOnAnte { return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") } return }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { count := msg.(msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) } cdc := codec.New() registerTestCodec(cdc) app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{ ConsensusParams: &abci.ConsensusParams{ Block: &abci.BlockParams{ MaxGas: 9, }, }, }) app.InitChain(abci.RequestInitChain{}) header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(5, 0) tx.setFailOnAnte(true) txBytes, err := cdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: 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.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: 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, err error) { 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, error) { store := ctx.KVStore(capKey1) store.Set(key, value) return &sdk.Result{}, nil }) } 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, err := app.Check(tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) require.Equal(t, 0, len(res.Value)) // query is still empty after a DeliverTx before we commit header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) _, resTx, err = app.Deliver(tx) require.NoError(t, err) require.NotNil(t, 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) } func TestGRPCQuery(t *testing.T) { grpcQueryOpt := func(bapp *BaseApp) { testdata.RegisterTestServiceServer( bapp.GRPCQueryRouter(), testdata.TestServiceImpl{}, ) } app := setupBaseApp(t, grpcQueryOpt) app.InitChain(abci.RequestInitChain{}) header := abci.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.Commit() req := testdata.SayHelloRequest{Name: "foo"} reqBz, err := req.Marshal() require.NoError(t, err) reqQuery := abci.RequestQuery{ Data: reqBz, Path: "/cosmos_sdk.codec.v1.TestService/SayHello", } resQuery := app.Query(reqQuery) require.Equal(t, abci.CodeTypeOK, resQuery.Code, resQuery) var res testdata.SayHelloResponse err = res.Unmarshal(resQuery.Value) require.NoError(t, err) require.Equal(t, "Hello foo!", res.Greeting) } // 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)} }) } idPeerFilterOpt := func(bapp *BaseApp) { bapp.SetIDPeerFilter(func(id string) abci.ResponseQuery { require.Equal(t, "testid", id) return abci.ResponseQuery{Code: uint32(4)} }) } app := setupBaseApp(t, addrPeerFilterOpt, idPeerFilterOpt) addrQuery := abci.RequestQuery{ Path: "/p2p/filter/addr/1.1.1.1:8000", } res := app.Query(addrQuery) require.Equal(t, uint32(3), res.Code) idQuery := abci.RequestQuery{ Path: "/p2p/filter/id/testid", } res = app.Query(idQuery) require.Equal(t, uint32(4), res.Code) } func TestGetMaximumBlockGas(t *testing.T) { app := setupBaseApp(t) app.InitChain(abci.RequestInitChain{}) ctx := app.NewContext(true, abci.Header{}) app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 0}}) require.Equal(t, uint64(0), app.getMaximumBlockGas(ctx)) app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -1}}) require.Equal(t, uint64(0), app.getMaximumBlockGas(ctx)) app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 5000000}}) require.Equal(t, uint64(5000000), app.getMaximumBlockGas(ctx)) app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -5000000}}) require.Panics(t, func() { app.getMaximumBlockGas(ctx) }) } // NOTE: represents a new custom router for testing purposes of WithRouter() type testCustomRouter struct { routes sync.Map } func (rtr *testCustomRouter) AddRoute(path string, h sdk.Handler) sdk.Router { rtr.routes.Store(path, h) return rtr } func (rtr *testCustomRouter) Route(ctx sdk.Context, path string) sdk.Handler { if v, ok := rtr.routes.Load(path); ok { if h, ok := v.(sdk.Handler); ok { return h } } return nil } func TestWithRouter(t *testing.T) { // test increments in the ante anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } // test increments in the handler deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { bapp.SetRouter(&testCustomRouter{routes: sync.Map{}}) bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder codec := codec.New() registerTestCodec(codec) nBlocks := 3 txPerHeight := 5 for blockN := 0; blockN < nBlocks; blockN++ { header := abci.Header{Height: int64(blockN) + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) } app.EndBlock(abci.RequestEndBlock{}) app.Commit() } }