diff --git a/CHANGELOG.md b/CHANGELOG.md index cc391bec7aa7..90673485c90a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/gov) [\#10373](https://github.com/cosmos/cosmos-sdk/pull/10373) Removed gov `keeper.{MustMarshal, MustUnmarshal}`. * [\#10348](https://github.com/cosmos/cosmos-sdk/pull/10348) StdSignBytes takes a new argument of type `*tx.Tip` for signing over tips using LEGACY_AMINO_JSON. * [\#10208](https://github.com/cosmos/cosmos-sdk/pull/10208) The `x/auth/signing.Tx` interface now also includes a new `GetTip() *tx.Tip` method for verifying tipped transactions. The `x/auth/types` expected BankKeeper interface now expects the `SendCoins` method too. +* [\#10612](https://github.com/cosmos/cosmos-sdk/pull/10612) `baseapp.NewBaseApp` constructor function doesn't take the `sdk.TxDecoder` anymore. This logic has been moved into the TxDecoderMiddleware. ### Client Breaking Changes diff --git a/baseapp/abci.go b/baseapp/abci.go index b3569ff8f53c..398673bd1662 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -250,13 +250,8 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type)) } - reqTx, err := app.txDecoder(req.Tx) - if err != nil { - return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) - } - ctx := app.getContextForTx(mode, req.Tx) - res, checkRes, err := app.txHandler.CheckTx(ctx, tx.Request{Tx: reqTx, TxBytes: req.Tx}, tx.RequestCheckTx{Type: req.Type}) + res, checkRes, err := app.txHandler.CheckTx(ctx, tx.Request{TxBytes: req.Tx}, tx.RequestCheckTx{Type: req.Type}) if err != nil { return sdkerrors.ResponseCheckTx(err, uint64(res.GasUsed), uint64(res.GasWanted), app.trace) } @@ -285,14 +280,9 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx } } }() - reqTx, err := app.txDecoder(req.Tx) - if err != nil { - abciRes = sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace) - return abciRes - } ctx := app.getContextForTx(runTxModeDeliver, req.Tx) - res, err := app.txHandler.DeliverTx(ctx, tx.Request{Tx: reqTx, TxBytes: req.Tx}) + res, err := app.txHandler.DeliverTx(ctx, tx.Request{TxBytes: req.Tx}) if err != nil { abciRes = sdkerrors.ResponseDeliverTx(err, uint64(res.GasUsed), uint64(res.GasWanted), app.trace) return abciRes diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index 2dee74b67e82..338c7c4d0cb3 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -25,20 +25,20 @@ func TestGetBlockRentionHeight(t *testing.T) { expected int64 }{ "defaults": { - bapp: baseapp.NewBaseApp(name, logger, db, nil), + bapp: baseapp.NewBaseApp(name, logger, db), maxAgeBlocks: 0, commitHeight: 499000, expected: 0, }, "pruning unbonding time only": { - bapp: baseapp.NewBaseApp(name, logger, db, nil, baseapp.SetMinRetainBlocks(1)), + bapp: baseapp.NewBaseApp(name, logger, db, baseapp.SetMinRetainBlocks(1)), maxAgeBlocks: 362880, commitHeight: 499000, expected: 136120, }, "pruning iavl snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(1), ), @@ -48,7 +48,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning state sync snapshot only": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), baseapp.SetMinRetainBlocks(1), @@ -59,7 +59,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning min retention only": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetMinRetainBlocks(400000), ), maxAgeBlocks: 0, @@ -68,7 +68,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "pruning all conditions": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -79,7 +79,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "no pruning due to no persisted state": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(400000), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -90,7 +90,7 @@ func TestGetBlockRentionHeight(t *testing.T) { }, "disable pruning": { bapp: baseapp.NewBaseApp( - name, logger, db, nil, + name, logger, db, baseapp.SetPruning(sdk.PruningOptions{KeepEvery: 10000}), baseapp.SetMinRetainBlocks(0), baseapp.SetSnapshotInterval(50000), baseapp.SetSnapshotKeepRecent(3), @@ -127,7 +127,7 @@ func TestBaseAppCreateQueryContextRejectsNegativeHeights(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) proves := []bool{ false, true, diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 07713ef4fe24..0f1a1263f5c2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -52,7 +52,6 @@ type BaseApp struct { // nolint: maligned queryRouter sdk.QueryRouter // router for redirecting query calls grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls interfaceRegistry types.InterfaceRegistry - txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx txHandler tx.Handler // txHandler for {Deliver,Check}Tx and simulations initChainer sdk.InitChainer // initialize state with validators and state blob @@ -137,7 +136,7 @@ type BaseApp struct { // nolint: maligned // // NOTE: The db is used to store the version number for now. func NewBaseApp( - name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), + name string, logger log.Logger, db dbm.DB, options ...func(*BaseApp), ) *BaseApp { app := &BaseApp{ logger: logger, @@ -147,7 +146,6 @@ func NewBaseApp( storeLoader: DefaultStoreLoader, queryRouter: NewQueryRouter(), grpcQueryRouter: NewGRPCQueryRouter(), - txDecoder: txDecoder, fauxMerkleMode: false, } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 46051b38237b..0bc643fd29ab 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/rootmulti" @@ -40,9 +41,13 @@ var ( capKey1 = sdk.NewKVStoreKey("key1") capKey2 = sdk.NewKVStoreKey("key2") - interfaceRegistry = testdata.NewTestInterfaceRegistry() + encCfg = simapp.MakeTestEncodingConfig() ) +func init() { + registerTestCodec(encCfg.Amino) +} + type paramStore struct { db *dbm.MemDB } @@ -90,13 +95,10 @@ func newBaseApp(name string, options ...func(*baseapp.BaseApp)) *baseapp.BaseApp db := dbm.NewMemDB() codec := codec.NewLegacyAmino() registerTestCodec(codec) - return baseapp.NewBaseApp(name, logger, db, testTxDecoder(codec), options...) + return baseapp.NewBaseApp(name, logger, db, options...) } func registerTestCodec(cdc *codec.LegacyAmino) { - // register Tx, Msg - sdk.RegisterLegacyAminoCodec(cdc) - // register test types cdc.RegisterConcrete(&txTest{}, "cosmos-sdk/baseapp/txTest", nil) cdc.RegisterConcrete(&msgCounter{}, "cosmos-sdk/baseapp/msgCounter", nil) @@ -106,10 +108,7 @@ func registerTestCodec(cdc *codec.LegacyAmino) { } // aminoTxEncoder creates a amino TxEncoder for testing purposes. -func aminoTxEncoder() sdk.TxEncoder { - cdc := codec.NewLegacyAmino() - registerTestCodec(cdc) - +func aminoTxEncoder(cdc *codec.LegacyAmino) sdk.TxEncoder { return legacytx.StdTxConfig{Cdc: cdc}.TxEncoder() } @@ -132,6 +131,7 @@ func setupBaseApp(t *testing.T, options ...func(*baseapp.BaseApp)) *baseapp.Base func testTxHandler(options middleware.TxHandlerOptions, customTxHandlerMiddleware handlerFun) tx.Handler { return middleware.ComposeMiddlewares( middleware.NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), + middleware.NewTxDecoderMiddleware(options.TxDecoder), middleware.GasTxMiddleware, middleware.RecoveryTxMiddleware, middleware.NewIndexEventsTxMiddleware(options.IndexEvents), @@ -161,7 +161,8 @@ func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { return ctx, nil }, ) @@ -200,7 +201,7 @@ func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options tx.Msgs = append(tx.Msgs, msgKeyValue{Key: key, Value: value}) keyCounter++ } - txBytes, err := codec.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) resp := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, resp.IsOK(), "%v", resp.String()) @@ -245,7 +246,7 @@ func TestLoadVersion(t *testing.T) { pruningOpt := baseapp.SetPruning(storetypes.PruneNothing) db := dbm.NewMemDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt) // make a cap key and mount the store err := app.LoadLatestVersion() // needed to make stores non-nil @@ -272,7 +273,7 @@ func TestLoadVersion(t *testing.T) { commitID2 := storetypes.CommitID{Version: 2, Hash: res.Data} // reload with LoadLatestVersion - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt) app.MountStores() err = app.LoadLatestVersion() require.Nil(t, err) @@ -280,7 +281,7 @@ func TestLoadVersion(t *testing.T) { // reload with LoadVersion, see if you can commit the same block and get // the same result - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt) err = app.LoadVersion(1) require.Nil(t, err) testLoadVersionHelper(t, app, int64(1), commitID1) @@ -359,7 +360,7 @@ func TestSetLoader(t *testing.T) { if tc.setLoader != nil { opts = append(opts, tc.setLoader) } - app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) err := app.LoadLatestVersion() require.Nil(t, err) @@ -381,7 +382,7 @@ func TestVersionSetterGetter(t *testing.T) { pruningOpt := baseapp.SetPruning(storetypes.PruneDefault) db := dbm.NewMemDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt) require.Equal(t, "", app.Version()) res := app.Query(abci.RequestQuery{Path: "app/version"}) @@ -401,7 +402,7 @@ func TestLoadVersionInvalid(t *testing.T) { pruningOpt := baseapp.SetPruning(storetypes.PruneNothing) db := dbm.NewMemDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt) err := app.LoadLatestVersion() require.Nil(t, err) @@ -416,7 +417,7 @@ func TestLoadVersionInvalid(t *testing.T) { commitID1 := storetypes.CommitID{Version: 1, Hash: res.Data} // create a new app with the stores mounted under the same cap key - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt) // require we can load the latest version err = app.LoadVersion(1) @@ -438,7 +439,7 @@ func TestLoadVersionPruning(t *testing.T) { pruningOpt := baseapp.SetPruning(pruningOptions) db := dbm.NewMemDB() name := t.Name() - app := baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app := baseapp.NewBaseApp(name, logger, db, pruningOpt) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("key1") @@ -476,7 +477,7 @@ func TestLoadVersionPruning(t *testing.T) { } // reload with LoadLatestVersion, check it loads last version - app = baseapp.NewBaseApp(name, logger, db, nil, pruningOpt) + app = baseapp.NewBaseApp(name, logger, db, pruningOpt) app.MountStores(capKey) err = app.LoadLatestVersion() @@ -494,7 +495,7 @@ func testLoadVersionHelper(t *testing.T, app *baseapp.BaseApp, expectedHeight in func TestOptionFunction(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() - bap := baseapp.NewBaseApp("starting name", logger, db, nil, testChangeNameHelper("new name")) + bap := baseapp.NewBaseApp("starting name", logger, db, testChangeNameHelper("new name")) require.Equal(t, bap.GetName(), "new name", "BaseApp should have had name changed via option function") } @@ -504,23 +505,6 @@ func testChangeNameHelper(name string) func(*baseapp.BaseApp) { } } -// Test that txs can be unmarshalled and read and that -// correct error codes are returned when not -func TestTxDecoder(t *testing.T) { - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - - app := newBaseApp(t.Name()) - tx := newTxCounter(1, 0) - txBytes := codec.MustMarshal(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()) @@ -589,7 +573,7 @@ func TestInitChainer(t *testing.T) { // we can reload the same app later db := dbm.NewMemDB() logger := defaultLogger() - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") app.MountStores(capKey, capKey2) @@ -644,7 +628,7 @@ func TestInitChainer(t *testing.T) { require.Equal(t, value, res.Value) // reload app - app = baseapp.NewBaseApp(name, logger, db, nil) + app = baseapp.NewBaseApp(name, logger, db) app.SetInitChainer(initChainer) app.MountStores(capKey, capKey2) err = app.LoadLatestVersion() // needed to make stores non-nil @@ -668,7 +652,7 @@ func TestInitChain_WithInitialHeight(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) app.InitChain( abci.RequestInitChain{ @@ -684,7 +668,7 @@ func TestBeginBlock_WithInitialHeight(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) app.InitChain( abci.RequestInitChain{ @@ -973,7 +957,8 @@ func TestCheckTx(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, customHandlerTxTest(t, capKey1, counterKey), ) @@ -985,17 +970,13 @@ func TestCheckTx(t *testing.T) { nTxs := int64(5) app.InitChain(abci.RequestInitChain{}) - // Create same codec used in txDecoder - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - for i := int64(0); i < nTxs; i++ { tx := newTxCounter(i, 0) // no messages - txBytes, err := codec.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(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)) + require.True(t, r.IsOK(), fmt.Sprintf("%+v", r)) } checkStateStore := app.CheckState().Context().KVStore(capKey1) @@ -1026,6 +1007,7 @@ func TestDeliverTx(t *testing.T) { anteKey := []byte("ante-key") // test increments in the handler deliverKey := []byte("deliver-key") + txHandlerOpt := func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) @@ -1033,7 +1015,8 @@ func TestDeliverTx(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, customHandlerTxTest(t, capKey1, anteKey), ) @@ -1042,10 +1025,6 @@ func TestDeliverTx(t *testing.T) { app := setupBaseApp(t, txHandlerOpt) app.InitChain(abci.RequestInitChain{}) - // Create same codec used in txDecoder - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - nBlocks := 3 txPerHeight := 5 @@ -1057,7 +1036,7 @@ func TestDeliverTx(t *testing.T) { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1086,6 +1065,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { // increment the msg counter deliverKey := []byte("deliver-key") deliverKey2 := []byte("deliver-key2") + txHandlerOpt := func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() r1 := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) @@ -1095,7 +1075,8 @@ func TestMultiMsgDeliverTx(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, customHandlerTxTest(t, capKey1, anteKey), ) @@ -1103,17 +1084,13 @@ func TestMultiMsgDeliverTx(t *testing.T) { } app := setupBaseApp(t, txHandlerOpt) - // Create same codec used in txDecoder - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - // run a multi-msg tx // with all msgs the same route header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(0, 0, 1, 2) - txBytes, err := codec.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -1133,7 +1110,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { tx = newTxCounter(1, 3) tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{1}) - txBytes, err = codec.Marshal(tx) + txBytes, err = encCfg.Amino.Marshal(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -1183,7 +1160,8 @@ func TestSimulateTx(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { return ctx, nil }, ) @@ -1193,10 +1171,6 @@ func TestSimulateTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) - // Create same codec used in txDecoder - cdc := codec.NewLegacyAmino() - registerTestCodec(cdc) - nBlocks := 3 for blockN := 0; blockN < nBlocks; blockN++ { count := int64(blockN + 1) @@ -1205,7 +1179,7 @@ func TestSimulateTx(t *testing.T) { tx := newTxCounter(count, count) tx.GasLimit = gasConsumed - txBytes, err := cdc.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.Nil(t, err) // simulate a message, check gas reported @@ -1258,7 +1232,8 @@ func TestRunInvalidTransaction(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { return @@ -1274,7 +1249,7 @@ func TestRunInvalidTransaction(t *testing.T) { // transaction with no messages { emptyTx := &txTest{} - _, result, err := app.SimDeliver(aminoTxEncoder(), emptyTx) + _, result, err := app.SimDeliver(aminoTxEncoder(encCfg.Amino), emptyTx) require.Nil(t, result) space, code, _ := sdkerrors.ABCIInfo(err, false) @@ -1300,7 +1275,7 @@ func TestRunInvalidTransaction(t *testing.T) { for _, testCase := range testCases { tx := testCase.tx - _, _, err := app.SimDeliver(aminoTxEncoder(), tx) + _, _, err := app.SimDeliver(aminoTxEncoder(encCfg.Amino), tx) if testCase.fail { require.Error(t, err) @@ -1317,7 +1292,7 @@ func TestRunInvalidTransaction(t *testing.T) { // transaction with no known route { unknownRouteTx := txTest{[]sdk.Msg{&msgNoRoute{}}, 0, false, math.MaxUint64} - _, result, err := app.SimDeliver(aminoTxEncoder(), unknownRouteTx) + _, result, err := app.SimDeliver(aminoTxEncoder(encCfg.Amino), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1326,7 +1301,7 @@ func TestRunInvalidTransaction(t *testing.T) { require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err) unknownRouteTx = txTest{[]sdk.Msg{&msgCounter{}, &msgNoRoute{}}, 0, false, math.MaxUint64} - _, result, err = app.SimDeliver(aminoTxEncoder(), unknownRouteTx) + _, result, err = app.SimDeliver(aminoTxEncoder(encCfg.Amino), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1340,8 +1315,10 @@ func TestRunInvalidTransaction(t *testing.T) { 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 + // new codec so we can encode the tx, but we shouldn't be able to decode, + // because baseapp's codec is not aware of msgNoDecode. newCdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(newCdc) // register Tx, Msg registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) @@ -1382,7 +1359,8 @@ func TestTxGasLimits(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, ante, ) @@ -1421,7 +1399,7 @@ func TestTxGasLimits(t *testing.T) { for i, tc := range testCases { tx := tc.tx tx.GasLimit = gasGranted - gInfo, result, err := app.SimDeliver(aminoTxEncoder(), tx) + gInfo, result, err := app.SimDeliver(aminoTxEncoder(encCfg.Amino), 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)) @@ -1469,7 +1447,8 @@ func TestMaxBlockGasLimits(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, ante, ) @@ -1514,7 +1493,7 @@ func TestMaxBlockGasLimits(t *testing.T) { // execute the transaction multiple times for j := 0; j < tc.numDelivers; j++ { - _, result, err := app.SimDeliver(aminoTxEncoder(), tx) + _, result, err := app.SimDeliver(aminoTxEncoder(encCfg.Amino), tx) ctx := app.DeliverState().Context() @@ -1546,7 +1525,6 @@ func TestMaxBlockGasLimits(t *testing.T) { func TestBaseAppMiddleware(t *testing.T) { anteKey := []byte("ante-key") deliverKey := []byte("deliver-key") - cdc := codec.NewLegacyAmino() txHandlerOpt := func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() @@ -1555,7 +1533,8 @@ func TestBaseAppMiddleware(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, customHandlerTxTest(t, capKey1, anteKey), ) @@ -1564,7 +1543,6 @@ func TestBaseAppMiddleware(t *testing.T) { app := setupBaseApp(t, txHandlerOpt) app.InitChain(abci.RequestInitChain{}) - registerTestCodec(cdc) header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) @@ -1575,7 +1553,7 @@ func TestBaseAppMiddleware(t *testing.T) { // the next txs ante handler execution (customHandlerTxTest). tx := newTxCounter(0, 0) tx.setFailOnAnte(true) - txBytes, err := cdc.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.Empty(t, res.Events) @@ -1590,7 +1568,7 @@ func TestBaseAppMiddleware(t *testing.T) { tx = newTxCounter(0, 0) tx.setFailOnHandler(true) - txBytes, err = cdc.Marshal(tx) + txBytes, err = encCfg.Amino.Marshal(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1606,7 +1584,7 @@ func TestBaseAppMiddleware(t *testing.T) { // implicitly checked by previous tx executions tx = newTxCounter(1, 0) - txBytes, err = cdc.Marshal(tx) + txBytes, err = encCfg.Amino.Marshal(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1635,9 +1613,6 @@ func TestGasConsumptionBadTx(t *testing.T) { return ctx, nil } - cdc := codec.NewLegacyAmino() - registerTestCodec(cdc) - txHandlerOpt := func(bapp *baseapp.BaseApp) { legacyRouter := middleware.NewLegacyRouter() r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { @@ -1649,7 +1624,8 @@ func TestGasConsumptionBadTx(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, ante, ) @@ -1673,7 +1649,7 @@ func TestGasConsumptionBadTx(t *testing.T) { tx := newTxCounter(5, 0) tx.GasLimit = gasWanted tx.setFailOnAnte(true) - txBytes, err := cdc.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1681,7 +1657,7 @@ func TestGasConsumptionBadTx(t *testing.T) { // require next tx to fail due to black gas limit tx = newTxCounter(5, 0) - txBytes, err = cdc.Marshal(tx) + txBytes, err = encCfg.Amino.Marshal(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1710,7 +1686,8 @@ func TestQuery(t *testing.T) { txHandler := testTxHandler( middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, - MsgServiceRouter: middleware.NewMsgServiceRouter(interfaceRegistry), + MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: testTxDecoder(encCfg.Amino), }, func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { store := ctx.KVStore(capKey1) @@ -1738,7 +1715,7 @@ func TestQuery(t *testing.T) { require.Equal(t, 0, len(res.Value)) // query is still empty after a CheckTx - _, resTx, err := app.SimCheck(aminoTxEncoder(), tx) + _, resTx, err := app.SimCheck(aminoTxEncoder(encCfg.Amino), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) @@ -1748,7 +1725,7 @@ func TestQuery(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - _, resTx, err = app.SimDeliver(aminoTxEncoder(), tx) + _, resTx, err = app.SimDeliver(aminoTxEncoder(encCfg.Amino), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) @@ -2030,16 +2007,15 @@ func TestWithRouter(t *testing.T) { customRouter := &testCustomRouter{routes: sync.Map{}} r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) customRouter.AddRoute(r) - txHandler := middleware.NewRunMsgsTxHandler(middleware.NewMsgServiceRouter(interfaceRegistry), customRouter) + txHandler := middleware.ComposeMiddlewares( + middleware.NewRunMsgsTxHandler(middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), customRouter), + middleware.NewTxDecoderMiddleware(testTxDecoder(encCfg.Amino)), + ) bapp.SetTxHandler(txHandler) } app := setupBaseApp(t, txHandlerOpt) app.InitChain(abci.RequestInitChain{}) - // Create same codec used in txDecoder - codec := codec.NewLegacyAmino() - registerTestCodec(codec) - nBlocks := 3 txPerHeight := 5 @@ -2051,7 +2027,7 @@ func TestWithRouter(t *testing.T) { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.Marshal(tx) + txBytes, err := encCfg.Amino.Marshal(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -2074,7 +2050,7 @@ func TestBaseApp_EndBlock(t *testing.T) { }, } - app := baseapp.NewBaseApp(name, logger, db, nil) + app := baseapp.NewBaseApp(name, logger, db) app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) app.InitChain(abci.RequestInitChain{ ConsensusParams: cp, diff --git a/baseapp/grpcrouter_test.go b/baseapp/grpcrouter_test.go index e22e77a61de0..ca7a6e9b5486 100644 --- a/baseapp/grpcrouter_test.go +++ b/baseapp/grpcrouter_test.go @@ -55,7 +55,7 @@ func TestRegisterQueryServiceTwice(t *testing.T) { db := dbm.NewMemDB() encCfg := simapp.MakeTestEncodingConfig() logger, _ := log.NewDefaultLogger("plain", "info", false) - app := baseapp.NewBaseApp("test", logger, db, encCfg.TxConfig.TxDecoder()) + app := baseapp.NewBaseApp("test", logger, db) app.SetInterfaceRegistry(encCfg.InterfaceRegistry) testdata.RegisterInterfaces(encCfg.InterfaceRegistry) diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index bc837e3cc595..6b770499c70b 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -36,13 +36,8 @@ func (app *BaseApp) SimCheck(txEncoder sdk.TxEncoder, sdkTx sdk.Tx) (sdk.GasInfo // Simulate executes a tx in simulate mode to get result and gas info. func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { - sdkTx, err := app.txDecoder(txBytes) - if err != nil { - return sdk.GasInfo{}, nil, err - } - ctx := app.getContextForTx(runTxModeSimulate, txBytes) - res, err := app.txHandler.SimulateTx(ctx, tx.Request{Tx: sdkTx, TxBytes: txBytes}) + res, err := app.txHandler.SimulateTx(ctx, tx.Request{TxBytes: txBytes}) gasInfo := sdk.GasInfo{ GasWanted: res.GasWanted, GasUsed: res.GasUsed, diff --git a/baseapp/util_test.go b/baseapp/util_test.go index 5f7504af85ec..7244aff8307a 100644 --- a/baseapp/util_test.go +++ b/baseapp/util_test.go @@ -45,13 +45,6 @@ func (app *BaseApp) GetName() string { return app.name } -// GetName return name. -// -// This method is only accessible in baseapp tests. -func (app *BaseApp) TxDecoder(txBytes []byte) (sdk.Tx, error) { - return app.txDecoder(txBytes) -} - // CreateQueryContext calls app's createQueryContext. // // This method is only accessible in baseapp tests. diff --git a/server/mock/app.go b/server/mock/app.go index ff4a58fa829c..8ef0ae405e51 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -23,6 +23,7 @@ import ( func testTxHandler(options middleware.TxHandlerOptions) tx.Handler { return middleware.ComposeMiddlewares( middleware.NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), + middleware.NewTxDecoderMiddleware(options.TxDecoder), middleware.GasTxMiddleware, middleware.RecoveryTxMiddleware, middleware.NewIndexEventsTxMiddleware(options.IndexEvents), @@ -42,7 +43,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", logger, db, decodeTx) + baseApp := bam.NewBaseApp("kvstore", logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStores(capKeyMainStore) @@ -59,6 +60,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { middleware.TxHandlerOptions{ LegacyRouter: legacyRouter, MsgServiceRouter: middleware.NewMsgServiceRouter(encCfg.InterfaceRegistry), + TxDecoder: decodeTx, }, ) baseApp.SetTxHandler(txHandler) diff --git a/simapp/app.go b/simapp/app.go index 3598f9de1f52..51306e498bd4 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -205,7 +205,7 @@ func NewSimApp( legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry - bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) + bApp := baseapp.NewBaseApp(appName, logger, db, baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(interfaceRegistry) @@ -431,6 +431,7 @@ func (app *SimApp) setTxHandler(txConfig client.TxConfig, indexEventsStr []strin FeegrantKeeper: app.FeeGrantKeeper, SignModeHandler: txConfig.SignModeHandler(), SigGasConsumer: authmiddleware.DefaultSigVerificationGasConsumer, + TxDecoder: txConfig.TxDecoder(), }) if err != nil { panic(err) diff --git a/simapp/app_test.go b/simapp/app_test.go index 7b51b0d835bd..fa8881f93d29 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -78,7 +78,7 @@ func TestRunMigrations(t *testing.T) { app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) // Create a new baseapp and configurator for the purpose of this test. - bApp := baseapp.NewBaseApp(appName, logger, db, encCfg.TxConfig.TxDecoder()) + bApp := baseapp.NewBaseApp(appName, logger, db) bApp.SetCommitMultiStoreTracer(nil) bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry) msr := authmiddleware.NewMsgServiceRouter(encCfg.InterfaceRegistry) diff --git a/types/tx/middleware.go b/types/tx/middleware.go index e67b783796d9..abe29be672d8 100644 --- a/types/tx/middleware.go +++ b/types/tx/middleware.go @@ -22,6 +22,19 @@ type ResponseSimulateTx struct { Result *sdk.Result } +// Request is the tx request type used in middlewares. +// At least one of Tx or TxBytes must be set. If only TxBytes is set, then +// Tx will be populated by the TxDecoderMiddleware. If only Tx is set, then +// some middlewares (such as signature verification) will fail. +// +// In practice, the middleware stack is called from {Check,Deliver}Tx, which +// only passes the TxBytes. Then, the TxDecoderMiddleware decodes the bytes +// into the Tx field. +type Request struct { + Tx sdk.Tx + TxBytes []byte +} + // Response is the tx response type used in middlewares. type Response struct { GasWanted uint64 @@ -34,12 +47,6 @@ type Response struct { Events []abci.Event } -// Request is the tx request type used in middlewares. -type Request struct { - Tx sdk.Tx - TxBytes []byte -} - // RequestCheckTx is the additional request type used in middlewares CheckTx // method. type RequestCheckTx struct { diff --git a/x/auth/middleware/basic_test.go b/x/auth/middleware/basic_test.go index 6e067306691a..1c44e292eb93 100644 --- a/x/auth/middleware/basic_test.go +++ b/x/auth/middleware/basic_test.go @@ -16,7 +16,7 @@ func (s *MWTestSuite) TestValidateBasic() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.ValidateBasicMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.ValidateBasicMiddleware) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() @@ -54,7 +54,7 @@ func (s *MWTestSuite) TestValidateBasic() { func (s *MWTestSuite) TestValidateMemo() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.ValidateMemoMiddleware(s.app.AccountKeeper)) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.ValidateMemoMiddleware(s.app.AccountKeeper)) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() @@ -90,7 +90,7 @@ func (s *MWTestSuite) TestConsumeGasForTxSize() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.ConsumeTxSizeGasMiddleware(s.app.AccountKeeper)) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.ConsumeTxSizeGasMiddleware(s.app.AccountKeeper)) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() @@ -174,7 +174,7 @@ func (s *MWTestSuite) TestConsumeGasForTxSize() { func (s *MWTestSuite) TestTxHeightTimeoutMiddleware() { ctx := s.SetupTest(true) - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.TxTimeoutHeightMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.TxTimeoutHeightMiddleware) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() diff --git a/x/auth/middleware/ext_test.go b/x/auth/middleware/ext_test.go index db2714536463..ec57f722c893 100644 --- a/x/auth/middleware/ext_test.go +++ b/x/auth/middleware/ext_test.go @@ -13,7 +13,7 @@ func (s *MWTestSuite) TestRejectExtensionOptionsMiddleware() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.RejectExtensionOptionsMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.RejectExtensionOptionsMiddleware) // no extension options should not trigger an error theTx := txBuilder.GetTx() diff --git a/x/auth/middleware/fee_test.go b/x/auth/middleware/fee_test.go index 3a60558e0da0..e4673f3f8dd5 100644 --- a/x/auth/middleware/fee_test.go +++ b/x/auth/middleware/fee_test.go @@ -13,7 +13,7 @@ func (s *MWTestSuite) TestEnsureMempoolFees() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.MempoolFeeMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.MempoolFeeMiddleware) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() @@ -55,7 +55,7 @@ func (s *MWTestSuite) TestDeductFees() { ctx := s.SetupTest(false) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.DeductFeeMiddleware( s.app.AccountKeeper, s.app.BankKeeper, diff --git a/x/auth/middleware/feegrant_test.go b/x/auth/middleware/feegrant_test.go index e9e1db2ced29..c4c429f32f5b 100644 --- a/x/auth/middleware/feegrant_test.go +++ b/x/auth/middleware/feegrant_test.go @@ -31,7 +31,7 @@ func (s *MWTestSuite) TestDeductFeesNoDelegation() { protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(app.InterfaceRegistry()), tx.DefaultSignModes) txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.DeductFeeMiddleware( s.app.AccountKeeper, s.app.BankKeeper, diff --git a/x/auth/middleware/gas_test.go b/x/auth/middleware/gas_test.go index f89492f16f6c..78685fa6c766 100644 --- a/x/auth/middleware/gas_test.go +++ b/x/auth/middleware/gas_test.go @@ -50,7 +50,7 @@ func (s *MWTestSuite) setupGasTx() (signing.Tx, []byte, sdk.Context, uint64) { func (s *MWTestSuite) TestSetup() { testTx, _, ctx, gasLimit := s.setupGasTx() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.GasTxMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.GasTxMiddleware) testcases := []struct { name string @@ -77,42 +77,43 @@ func (s *MWTestSuite) TestSetup() { func (s *MWTestSuite) TestRecoverPanic() { testTx, txBytes, ctx, gasLimit := s.setupGasTx() - txHandler := middleware.ComposeMiddlewares(outOfGasTxHandler{}, middleware.GasTxMiddleware, middleware.RecoveryTxMiddleware) + txHandler := middleware.ComposeMiddlewares(outOfGasTxHandler, middleware.GasTxMiddleware, middleware.RecoveryTxMiddleware) res, _, err := txHandler.CheckTx(sdk.WrapSDKContext(ctx), tx.Request{Tx: testTx, TxBytes: txBytes}, tx.RequestCheckTx{}) s.Require().Error(err, "Did not return error on OutOfGas panic") s.Require().True(errors.Is(sdkerrors.ErrOutOfGas, err), "Returned error is not an out of gas error") s.Require().Equal(gasLimit, uint64(res.GasWanted)) - txHandler = middleware.ComposeMiddlewares(outOfGasTxHandler{}, middleware.GasTxMiddleware) + txHandler = middleware.ComposeMiddlewares(outOfGasTxHandler, middleware.GasTxMiddleware) s.Require().Panics(func() { txHandler.CheckTx(sdk.WrapSDKContext(ctx), tx.Request{Tx: testTx, TxBytes: txBytes}, tx.RequestCheckTx{}) }, "Recovered from non-Out-of-Gas panic") } -// outOfGasTxHandler is a test middleware that will throw OutOfGas panic. -type outOfGasTxHandler struct{} - -var _ tx.Handler = outOfGasTxHandler{} - -func (txh outOfGasTxHandler) DeliverTx(ctx context.Context, _ tx.Request) (tx.Response, error) { - sdkCtx := sdk.UnwrapSDKContext(ctx) - overLimit := sdkCtx.GasMeter().Limit() + 1 +// customTxHandler is a test middleware that will run a custom function. +type customTxHandler struct { + fn func(context.Context, tx.Request) (tx.Response, error) +} - // Should panic with outofgas error - sdkCtx.GasMeter().ConsumeGas(overLimit, "test panic") +var _ tx.Handler = customTxHandler{} - panic("not reached") +func (h customTxHandler) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + return h.fn(ctx, req) +} +func (h customTxHandler) CheckTx(ctx context.Context, req tx.Request, _ tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + res, err := h.fn(ctx, req) + return res, tx.ResponseCheckTx{}, err +} +func (h customTxHandler) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + return h.fn(ctx, req) } -func (txh outOfGasTxHandler) CheckTx(ctx context.Context, _ tx.Request, _ tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { - sdkCtx := sdk.UnwrapSDKContext(ctx) - overLimit := sdkCtx.GasMeter().Limit() + 1 - // Should panic with outofgas error - sdkCtx.GasMeter().ConsumeGas(overLimit, "test panic") +// noopTxHandler is a test middleware that returns an empty response. +var noopTxHandler = customTxHandler{func(_ context.Context, _ tx.Request) (tx.Response, error) { + return tx.Response{}, nil +}} - panic("not reached") -} -func (txh outOfGasTxHandler) SimulateTx(ctx context.Context, _ tx.Request) (tx.Response, error) { +// outOfGasTxHandler is a test middleware that panics with an outOfGas error. +var outOfGasTxHandler = customTxHandler{func(ctx context.Context, _ tx.Request) (tx.Response, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) overLimit := sdkCtx.GasMeter().Limit() + 1 @@ -120,19 +121,4 @@ func (txh outOfGasTxHandler) SimulateTx(ctx context.Context, _ tx.Request) (tx.R sdkCtx.GasMeter().ConsumeGas(overLimit, "test panic") panic("not reached") -} - -// noopTxHandler is a test middleware that does nothing. -type noopTxHandler struct{} - -var _ tx.Handler = noopTxHandler{} - -func (txh noopTxHandler) CheckTx(_ context.Context, _ tx.Request, _ tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { - return tx.Response{}, tx.ResponseCheckTx{}, nil -} -func (txh noopTxHandler) SimulateTx(_ context.Context, _ tx.Request) (tx.Response, error) { - return tx.Response{}, nil -} -func (txh noopTxHandler) DeliverTx(ctx context.Context, _ tx.Request) (tx.Response, error) { - return tx.Response{}, nil -} +}} diff --git a/x/auth/middleware/middleware.go b/x/auth/middleware/middleware.go index a17cdd291ed7..53d16938e219 100644 --- a/x/auth/middleware/middleware.go +++ b/x/auth/middleware/middleware.go @@ -32,6 +32,10 @@ func ComposeMiddlewares(txHandler tx.Handler, middlewares ...tx.Middleware) tx.H type TxHandlerOptions struct { Debug bool + + // TxDecoder is used to decode the raw tx bytes into a sdk.Tx. + TxDecoder sdk.TxDecoder + // IndexEvents defines the set of events in the form {eventType}.{attributeKey}, // which informs Tendermint what to index. If empty, all events will be indexed. IndexEvents map[string]struct{} @@ -49,6 +53,10 @@ type TxHandlerOptions struct { // NewDefaultTxHandler defines a TxHandler middleware stacks that should work // for most applications. func NewDefaultTxHandler(options TxHandlerOptions) (tx.Handler, error) { + if options.TxDecoder == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "txDecoder is required for middlewares") + } + if options.AccountKeeper == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for middlewares") } @@ -68,6 +76,7 @@ func NewDefaultTxHandler(options TxHandlerOptions) (tx.Handler, error) { return ComposeMiddlewares( NewRunMsgsTxHandler(options.MsgServiceRouter, options.LegacyRouter), + NewTxDecoderMiddleware(options.TxDecoder), // Set a new GasMeter on sdk.Context. // // Make sure the Gas middleware is outside of all other middlewares diff --git a/x/auth/middleware/middleware_test.go b/x/auth/middleware/middleware_test.go index 417ac8e7fcaa..d923b84b2c3d 100644 --- a/x/auth/middleware/middleware_test.go +++ b/x/auth/middleware/middleware_test.go @@ -1025,6 +1025,7 @@ func (s *MWTestSuite) TestCustomSignatureVerificationGasConsumer() { return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey) } }, + TxDecoder: s.clientCtx.TxConfig.TxDecoder(), }, ) s.Require().NoError(err) diff --git a/x/auth/middleware/priority_test.go b/x/auth/middleware/priority_test.go index a04e2065d971..388418644040 100644 --- a/x/auth/middleware/priority_test.go +++ b/x/auth/middleware/priority_test.go @@ -12,7 +12,7 @@ func (s *MWTestSuite) TestPriority() { ctx := s.SetupTest(true) // setup txBuilder := s.clientCtx.TxConfig.NewTxBuilder() - txHandler := middleware.ComposeMiddlewares(noopTxHandler{}, middleware.TxPriorityMiddleware) + txHandler := middleware.ComposeMiddlewares(noopTxHandler, middleware.TxPriorityMiddleware) // keys and addresses priv1, _, addr1 := testdata.KeyTestPubAddr() diff --git a/x/auth/middleware/sigverify_test.go b/x/auth/middleware/sigverify_test.go index b5f4d9c15966..01e8898336c7 100644 --- a/x/auth/middleware/sigverify_test.go +++ b/x/auth/middleware/sigverify_test.go @@ -27,7 +27,7 @@ func (s *MWTestSuite) TestSetPubKey() { txBuilder := s.clientCtx.TxConfig.NewTxBuilder() require := s.Require() txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.SetPubKeyMiddleware(s.app.AccountKeeper), ) @@ -127,7 +127,7 @@ func (s *MWTestSuite) TestSigVerification() { // make block height non-zero to ensure account numbers part of signBytes ctx = ctx.WithBlockHeight(1) txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.SetPubKeyMiddleware(s.app.AccountKeeper), middleware.SigVerificationMiddleware( s.app.AccountKeeper, @@ -239,7 +239,7 @@ func (s *MWTestSuite) TestSigVerification_ExplicitAmino() { gasLimit := testdata.NewTestGasLimit() txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.SetPubKeyMiddleware(s.app.AccountKeeper), middleware.SigVerificationMiddleware( s.app.AccountKeeper, @@ -342,7 +342,7 @@ func (s *MWTestSuite) runSigMiddlewares(params types.Params, _ bool, privs ...cr s.Require().NoError(err) txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.SetPubKeyMiddleware(s.app.AccountKeeper), middleware.SigGasConsumeMiddleware(s.app.AccountKeeper, middleware.DefaultSigVerificationGasConsumer), middleware.SigVerificationMiddleware( @@ -382,7 +382,7 @@ func (s *MWTestSuite) TestIncrementSequenceMiddleware() { s.Require().NoError(err) txHandler := middleware.ComposeMiddlewares( - noopTxHandler{}, + noopTxHandler, middleware.IncrementSequenceMiddleware(s.app.AccountKeeper), ) diff --git a/x/auth/middleware/testutil_test.go b/x/auth/middleware/testutil_test.go index 9d804fa17ae2..241615c488df 100644 --- a/x/auth/middleware/testutil_test.go +++ b/x/auth/middleware/testutil_test.go @@ -89,6 +89,7 @@ func (s *MWTestSuite) SetupTest(isCheckTx bool) sdk.Context { FeegrantKeeper: s.app.FeeGrantKeeper, SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), SigGasConsumer: middleware.DefaultSigVerificationGasConsumer, + TxDecoder: s.clientCtx.TxConfig.TxDecoder(), }) s.Require().NoError(err) s.txHandler = txHandler diff --git a/x/auth/middleware/tx.go b/x/auth/middleware/tx.go new file mode 100644 index 000000000000..f543524f3fa7 --- /dev/null +++ b/x/auth/middleware/tx.go @@ -0,0 +1,77 @@ +package middleware + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +type txDecoderHandler struct { + next tx.Handler + txDecoder sdk.TxDecoder +} + +// NewTxDecoderMiddleware creates a new middleware that will decode tx bytes +// into a sdk.Tx. As input request, at least one of Tx or TxBytes must be set. +// If only TxBytes is set, then TxDecoderMiddleware will populate the Tx field. +// If only Tx is set, then TxBytes will be left empty, but some middlewares +// such as signature verification might fail. +func NewTxDecoderMiddleware(txDecoder sdk.TxDecoder) tx.Middleware { + return func(txh tx.Handler) tx.Handler { + return txDecoderHandler{next: txh, txDecoder: txDecoder} + } +} + +var _ tx.Handler = gasTxHandler{} + +// CheckTx implements tx.Handler.CheckTx. +func (h txDecoderHandler) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) { + newReq, err := h.populateReq(req) + if err != nil { + return tx.Response{}, tx.ResponseCheckTx{}, err + } + + return h.next.CheckTx(ctx, newReq, checkReq) +} + +// DeliverTx implements tx.Handler.DeliverTx. +func (h txDecoderHandler) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) { + newReq, err := h.populateReq(req) + if err != nil { + return tx.Response{}, err + } + + return h.next.DeliverTx(ctx, newReq) +} + +// SimulateTx implements tx.Handler.SimulateTx method. +func (h txDecoderHandler) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) { + newReq, err := h.populateReq(req) + if err != nil { + return tx.Response{}, err + } + + return h.next.SimulateTx(ctx, newReq) +} + +// populateReq takes a tx.Request, and if its Tx field is not set, then +// decodes the TxBytes and populates the decoded Tx field. It leaves +// req.TxBytes untouched. +func (h txDecoderHandler) populateReq(req tx.Request) (tx.Request, error) { + if len(req.TxBytes) == 0 && req.Tx == nil { + return tx.Request{}, sdkerrors.ErrInvalidRequest.Wrap("got empty tx request") + } + + sdkTx := req.Tx + var err error + if len(req.TxBytes) != 0 { + sdkTx, err = h.txDecoder(req.TxBytes) + if err != nil { + return tx.Request{}, err + } + } + + return tx.Request{Tx: sdkTx, TxBytes: req.TxBytes}, nil +} diff --git a/x/auth/middleware/tx_test.go b/x/auth/middleware/tx_test.go new file mode 100644 index 000000000000..8624aadbfea2 --- /dev/null +++ b/x/auth/middleware/tx_test.go @@ -0,0 +1,57 @@ +package middleware_test + +import ( + "context" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/x/auth/middleware" +) + +func (s *MWTestSuite) TestTxDecoderMiddleware() { + ctx := s.SetupTest(true) // setup + require := s.Require() + + // Create a tx. + priv1, _, addr1 := testdata.KeyTestPubAddr() + txBuilder := s.clientCtx.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs(testdata.NewTestMsg(addr1)) + require.NoError(err) + sdkTx, txBz, err := s.createTestTx(txBuilder, []cryptotypes.PrivKey{priv1}, []uint64{1}, []uint64{0}, ctx.ChainID()) + require.NoError(err) + + // Create a custom tx.Handler that checks that the req.Tx field is + // correctly populated. + txReqChecker := customTxHandler{func(c context.Context, r tx.Request) (tx.Response, error) { + require.NotNil(r.Tx) + require.Equal(sdkTx.GetMsgs()[0], r.Tx.GetMsgs()[0]) + return tx.Response{}, nil + }} + + testcases := []struct { + name string + req tx.Request + expErr bool + }{ + {"empty tx bz", tx.Request{}, true}, + {"tx bz and tx both given as inputs", tx.Request{Tx: sdkTx, TxBytes: txBz}, false}, + {"tx bz only given as input", tx.Request{TxBytes: txBz}, false}, + {"tx only given as input", tx.Request{Tx: sdkTx}, false}, + } + for _, tc := range testcases { + s.Run(tc.name, func() { + txHandler := middleware.ComposeMiddlewares( + txReqChecker, + middleware.NewTxDecoderMiddleware(s.clientCtx.TxConfig.TxDecoder()), + ) + _, err := txHandler.DeliverTx(sdk.WrapSDKContext(ctx), tc.req) + if tc.expErr { + require.Error(err) + } else { + require.NoError(err) + } + }) + } +} diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index 4389f3da7838..341a2ffe30da 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -124,7 +124,7 @@ func TestSetLoader(t *testing.T) { // load the app with the existing db opts := []func(*baseapp.BaseApp){baseapp.SetPruning(storetypes.PruneNothing)} - origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) origapp.MountStores(sdk.NewKVStoreKey(tc.origStoreKey)) err := origapp.LoadLatestVersion() require.Nil(t, err) @@ -140,7 +140,7 @@ func TestSetLoader(t *testing.T) { } // load the new app with the original app db - app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, opts...) app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) err = app.LoadLatestVersion() require.Nil(t, err)