improve error messages for legacy rest endpoints (#7856)

* improve error messages for legacy rest endpoints

* add test

* review changes

* review changes

* refactor

* Update x/auth/client/rest/query.go

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
atheeshp 2020-11-17 20:16:24 +05:30 committed by GitHub
parent 9369a00557
commit ab7104865d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 0 deletions

View File

@ -55,6 +55,12 @@ func DecodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
response := DecodeResp(stdTx)
err = checkSignModeError(w, clientCtx, response, "/cosmos/tx/v1beta1/txs/decode")
if err != nil {
// Error is already returned by checkSignModeError.
return
}
rest.PostProcessResponse(w, clientCtx, response)
}
}

View File

@ -17,6 +17,8 @@ import (
genutilrest "github.com/cosmos/cosmos-sdk/x/genutil/client/rest"
)
const unRegisteredConcreteTypeErr = "unregistered concrete type"
// query accountREST Handler
func QueryAccountRequestHandlerFn(storeName string, clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@ -107,6 +109,12 @@ func QueryTxsRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
packStdTxResponse(w, clientCtx, txRes)
}
err = checkSignModeError(w, clientCtx, searchResult, "/cosmos/tx/v1beta1/txs")
if err != nil {
// Error is already returned by checkSignModeError.
return
}
rest.PostProcessResponseBare(w, clientCtx, searchResult)
}
}
@ -143,6 +151,12 @@ func QueryTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr))
}
err = checkSignModeError(w, clientCtx, output, "/cosmos/tx/v1beta1/tx/{txhash}")
if err != nil {
// Error is already returned by checkSignModeError.
return
}
rest.PostProcessResponseBare(w, clientCtx, output)
}
}
@ -182,3 +196,19 @@ func packStdTxResponse(w http.ResponseWriter, clientCtx client.Context, txRes *s
return nil
}
func checkSignModeError(w http.ResponseWriter, ctx client.Context, resp interface{}, grpcEndPoint string) error {
// LegacyAmino used intentionally here to handle the SignMode errors
marshaler := ctx.LegacyAmino
_, err := marshaler.MarshalJSON(resp)
if err != nil && strings.Contains(err.Error(), unRegisteredConcreteTypeErr) {
rest.WriteErrorResponse(w, http.StatusInternalServerError,
"This transaction was created with the new SIGN_MODE_DIRECT signing method, and therefore cannot be displayed"+
" via legacy REST handlers, please use CLI or directly query the Tendermint RPC endpoint to query"+
" this transaction. gRPC gateway endpoint is "+grpcEndPoint)
return err
}
return nil
}

View File

@ -10,14 +10,19 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
ibccli "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/cli"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
rest2 "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
"github.com/cosmos/cosmos-sdk/x/bank/types"
@ -298,6 +303,75 @@ func (s *IntegrationTestSuite) broadcastReq(stdTx legacytx.StdTx, mode string) (
return rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", bz)
}
func (s *IntegrationTestSuite) TestLegacyRestErrMessages() {
val := s.network.Validators[0]
args := []string{
"121", // dummy port-id
"21212121212", // dummy channel-id
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=foobar", flags.FlagMemo),
}
// created a dummy txn for IBC, eventually it fails. Our intension is to test the error message of querying a
// message which is signed with proto, since IBC won't support legacy amino at all we are considering a message from IBC module.
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, ibccli.NewChannelCloseInitCmd(), args)
s.Require().NoError(err)
var txRes sdk.TxResponse
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes))
s.Require().NoError(s.network.WaitForNextBlock())
// try to fetch the txn using legacy rest, this won't work since the ibc module doesn't support amino.
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash))
s.Require().NoError(err)
var errResp rest.ErrorResponse
s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &errResp))
errMsg := "This transaction was created with the new SIGN_MODE_DIRECT signing method, " +
"and therefore cannot be displayed via legacy REST handlers, please use CLI or directly query the Tendermint " +
"RPC endpoint to query this transaction."
s.Require().Contains(errResp.Error, errMsg)
// try fetching the txn using gRPC req, it will fetch info since it has proto codec.
grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/tx/%s", val.APIAddress, txRes.TxHash))
s.Require().NoError(err)
var getTxRes txtypes.GetTxResponse
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(grpcJSON, &getTxRes))
s.Require().Equal(getTxRes.Tx.Body.Memo, "foobar")
// generate broadcast only txn.
args = append(args, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly))
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, ibccli.NewChannelCloseInitCmd(), args)
s.Require().NoError(err)
txFile, cleanup := testutil.WriteToNewTempFile(s.T(), string(out.Bytes()))
txFileName := txFile.Name()
s.T().Cleanup(cleanup)
// encode the generated txn.
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.GetEncodeCommand(), []string{txFileName})
s.Require().NoError(err)
bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(rest2.DecodeReq{Tx: string(out.Bytes())})
s.Require().NoError(err)
// try to decode the txn using legacy rest, it fails.
res, err := rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz)
s.Require().NoError(err)
s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &errResp))
s.Require().Contains(errResp.Error, errMsg)
}
func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}