diff --git a/CHANGELOG.md b/CHANGELOG.md index 17c70cb718f6..3e82d2bd46ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (gRPC) [\#9015](https://github.com/cosmos/cosmos-sdk/pull/9015) Fix invalid status code when accessing gRPC endpoints. * (gRPC) [\#8945](https://github.com/cosmos/cosmos-sdk/pull/8945) gRPC reflection now works correctly. * (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic` * (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger diff --git a/client/query.go b/client/query.go index bb65d9bf3833..e79779ce571b 100644 --- a/client/query.go +++ b/client/query.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" abci "github.com/tendermint/tendermint/abci/types" tmbytes "github.com/tendermint/tendermint/libs/bytes" @@ -13,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // GetNode returns an RPC client. If the context's client is not defined, an @@ -84,7 +87,7 @@ func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) } if !result.Response.IsOK() { - return abci.ResponseQuery{}, errors.New(result.Response.Log) + return abci.ResponseQuery{}, sdkErrorToGRPCError(result.Response) } // data from trusted node or subspace query doesn't need verification @@ -95,6 +98,19 @@ func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) return result.Response, nil } +func sdkErrorToGRPCError(resp abci.ResponseQuery) error { + switch resp.Code { + case sdkerrors.ErrInvalidRequest.ABCICode(): + return status.Error(codes.InvalidArgument, resp.Log) + case sdkerrors.ErrUnauthorized.ABCICode(): + return status.Error(codes.Unauthenticated, resp.Log) + case sdkerrors.ErrKeyNotFound.ABCICode(): + return status.Error(codes.NotFound, resp.Log) + default: + return status.Error(codes.Unknown, resp.Log) + } +} + // query performs a query to a Tendermint node with the provided store name // and path. It returns the result and height of the query upon success // or an error if the query fails. diff --git a/x/staking/client/rest/grpc_query_test.go b/x/staking/client/rest/grpc_query_test.go index 69e9f34adc23..6e98844c4cb4 100644 --- a/x/staking/client/rest/grpc_query_test.go +++ b/x/staking/client/rest/grpc_query_test.go @@ -4,13 +4,16 @@ package rest_test import ( "fmt" + "io/ioutil" + "net/http" "testing" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" @@ -411,6 +414,35 @@ func (s *IntegrationTestSuite) TestQueryUnbondingDelegationGRPC() { } } +func (s *IntegrationTestSuite) TestQueryDelegationsResponseCode() { + val := s.network.Validators[0] + + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("test", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + s.T().Log("expect 404 error for address without delegations") + res, statusCode, err := getRequest(fmt.Sprintf("%s/cosmos/staking/v1beta1/delegations/%s", val.APIAddress, newAddr.String())) + s.Require().NoError(err) + s.Require().Contains(string(res), "\"code\": 5") + s.Require().Equal(404, statusCode) +} + +func getRequest(url string) ([]byte, int, error) { + res, err := http.Get(url) // nolint:gosec + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, res.StatusCode, err + } + + if err = res.Body.Close(); err != nil { + return nil, res.StatusCode, err + } + + return body, res.StatusCode, nil +} + func (s *IntegrationTestSuite) TestQueryDelegatorDelegationsGRPC() { val := s.network.Validators[0] baseURL := val.APIAddress