github.com/cosmos/cosmos-sdk@v0.50.10/client/query.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/cockroachdb/errors" 9 abci "github.com/cometbft/cometbft/abci/types" 10 rpcclient "github.com/cometbft/cometbft/rpc/client" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 14 "cosmossdk.io/store/rootmulti" 15 16 sdk "github.com/cosmos/cosmos-sdk/types" 17 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 18 ) 19 20 // GetNode returns an RPC client. If the context's client is not defined, an 21 // error is returned. 22 func (ctx Context) GetNode() (CometRPC, error) { 23 if ctx.Client == nil { 24 return nil, errors.New("no RPC client is defined in offline mode") 25 } 26 27 return ctx.Client, nil 28 } 29 30 // Query performs a query to a CometBFT node with the provided path. 31 // It returns the result and height of the query upon success or an error if 32 // the query fails. 33 func (ctx Context) Query(path string) ([]byte, int64, error) { 34 return ctx.query(path, nil) 35 } 36 37 // QueryWithData performs a query to a CometBFT node with the provided path 38 // and a data payload. It returns the result and height of the query upon success 39 // or an error if the query fails. 40 func (ctx Context) QueryWithData(path string, data []byte) ([]byte, int64, error) { 41 return ctx.query(path, data) 42 } 43 44 // QueryStore performs a query to a CometBFT node with the provided key and 45 // store name. It returns the result and height of the query upon success 46 // or an error if the query fails. 47 func (ctx Context) QueryStore(key []byte, storeName string) ([]byte, int64, error) { 48 return ctx.queryStore(key, storeName, "key") 49 } 50 51 // QueryABCI performs a query to a CometBFT node with the provide RequestQuery. 52 // It returns the ResultQuery obtained from the query. The height used to perform 53 // the query is the RequestQuery Height if it is non-zero, otherwise the context 54 // height is used. 55 func (ctx Context) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { 56 return ctx.queryABCI(req) 57 } 58 59 // GetFromAddress returns the from address from the context's name. 60 func (ctx Context) GetFromAddress() sdk.AccAddress { 61 return ctx.FromAddress 62 } 63 64 // GetFeePayerAddress returns the fee granter address from the context 65 func (ctx Context) GetFeePayerAddress() sdk.AccAddress { 66 return ctx.FeePayer 67 } 68 69 // GetFeeGranterAddress returns the fee granter address from the context 70 func (ctx Context) GetFeeGranterAddress() sdk.AccAddress { 71 return ctx.FeeGranter 72 } 73 74 // GetFromName returns the key name for the current context. 75 func (ctx Context) GetFromName() string { 76 return ctx.FromName 77 } 78 79 func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { 80 node, err := ctx.GetNode() 81 if err != nil { 82 return abci.ResponseQuery{}, err 83 } 84 85 var queryHeight int64 86 if req.Height != 0 { 87 queryHeight = req.Height 88 } else { 89 // fallback on the context height 90 queryHeight = ctx.Height 91 } 92 93 opts := rpcclient.ABCIQueryOptions{ 94 Height: queryHeight, 95 Prove: req.Prove, 96 } 97 98 result, err := node.ABCIQueryWithOptions(context.Background(), req.Path, req.Data, opts) 99 if err != nil { 100 return abci.ResponseQuery{}, err 101 } 102 103 if !result.Response.IsOK() { 104 return abci.ResponseQuery{}, sdkErrorToGRPCError(result.Response) 105 } 106 107 // data from trusted node or subspace query doesn't need verification 108 if !opts.Prove || !isQueryStoreWithProof(req.Path) { 109 return result.Response, nil 110 } 111 112 return result.Response, nil 113 } 114 115 func sdkErrorToGRPCError(resp abci.ResponseQuery) error { 116 switch resp.Code { 117 case sdkerrors.ErrInvalidRequest.ABCICode(): 118 return status.Error(codes.InvalidArgument, resp.Log) 119 case sdkerrors.ErrUnauthorized.ABCICode(): 120 return status.Error(codes.Unauthenticated, resp.Log) 121 case sdkerrors.ErrKeyNotFound.ABCICode(): 122 return status.Error(codes.NotFound, resp.Log) 123 default: 124 return status.Error(codes.Unknown, resp.Log) 125 } 126 } 127 128 // query performs a query to a CometBFT node with the provided store name 129 // and path. It returns the result and height of the query upon success 130 // or an error if the query fails. 131 func (ctx Context) query(path string, key []byte) ([]byte, int64, error) { 132 resp, err := ctx.queryABCI(abci.RequestQuery{ 133 Path: path, 134 Data: key, 135 Height: ctx.Height, 136 }) 137 if err != nil { 138 return nil, 0, err 139 } 140 141 return resp.Value, resp.Height, nil 142 } 143 144 // queryStore performs a query to a CometBFT node with the provided a store 145 // name and path. It returns the result and height of the query upon success 146 // or an error if the query fails. 147 func (ctx Context) queryStore(key []byte, storeName, endPath string) ([]byte, int64, error) { 148 path := fmt.Sprintf("/store/%s/%s", storeName, endPath) 149 return ctx.query(path, key) 150 } 151 152 // isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath> 153 // queryType must be "store" and subpath must be "key" to require a proof. 154 func isQueryStoreWithProof(path string) bool { 155 if !strings.HasPrefix(path, "/") { 156 return false 157 } 158 159 paths := strings.SplitN(path[1:], "/", 3) 160 161 switch { 162 case len(paths) != 3: 163 return false 164 case paths[0] != "store": 165 return false 166 case rootmulti.RequireProof("/" + paths[2]): 167 return true 168 } 169 170 return false 171 }