github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-021-protobuf-query-encoding.md (about) 1 # ADR 021: Protocol Buffer Query Encoding 2 3 ## Changelog 4 5 * 2020 March 27: Initial Draft 6 7 ## Status 8 9 Accepted 10 11 ## Context 12 13 This ADR is a continuation of the motivation, design, and context established in 14 [ADR 019](./adr-019-protobuf-state-encoding.md) and 15 [ADR 020](./adr-020-protobuf-transaction-encoding.md), namely, we aim to design the 16 Protocol Buffer migration path for the client-side of the Cosmos SDK. 17 18 This ADR continues from [ADD 020](./adr-020-protobuf-transaction-encoding.md) 19 to specify the encoding of queries. 20 21 ## Decision 22 23 ### Custom Query Definition 24 25 Modules define custom queries through a protocol buffers `service` definition. 26 These `service` definitions are generally associated with and used by the 27 GRPC protocol. However, the protocol buffers specification indicates that 28 they can be used more generically by any request/response protocol that uses 29 protocol buffer encoding. Thus, we can use `service` definitions for specifying 30 custom ABCI queries and even reuse a substantial amount of the GRPC infrastructure. 31 32 Each module with custom queries should define a service canonically named `Query`: 33 34 ```protobuf 35 // x/bank/types/types.proto 36 37 service Query { 38 rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { } 39 rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { } 40 } 41 ``` 42 43 #### Handling of Interface Types 44 45 Modules that use interface types and need true polymorphism generally force a 46 `oneof` up to the app-level that provides the set of concrete implementations of 47 that interface that the app supports. While app's are welcome to do the same for 48 queries and implement an app-level query service, it is recommended that modules 49 provide query methods that expose these interfaces via `google.protobuf.Any`. 50 There is a concern on the transaction level that the overhead of `Any` is too 51 high to justify its usage. However for queries this is not a concern, and 52 providing generic module-level queries that use `Any` does not preclude apps 53 from also providing app-level queries that return use the app-level `oneof`s. 54 55 A hypothetical example for the `gov` module would look something like: 56 57 ```protobuf 58 // x/gov/types/types.proto 59 60 import "google/protobuf/any.proto"; 61 62 service Query { 63 rpc GetProposal(GetProposalParams) returns (AnyProposal) { } 64 } 65 66 message AnyProposal { 67 ProposalBase base = 1; 68 google.protobuf.Any content = 2; 69 } 70 ``` 71 72 ### Custom Query Implementation 73 74 In order to implement the query service, we can reuse the existing [gogo protobuf](https://github.com/cosmos/gogoproto) 75 grpc plugin, which for a service named `Query` generates an interface named 76 `QueryServer` as below: 77 78 ```go 79 type QueryServer interface { 80 QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) 81 QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) 82 } 83 ``` 84 85 The custom queries for our module are implemented by implementing this interface. 86 87 The first parameter in this generated interface is a generic `context.Context`, 88 whereas querier methods generally need an instance of `sdk.Context` to read 89 from the store. Since arbitrary values can be attached to `context.Context` 90 using the `WithValue` and `Value` methods, the Cosmos SDK should provide a function 91 `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided 92 `context.Context`. 93 94 An example implementation of `QueryBalance` for the bank module as above would 95 look something like: 96 97 ```go 98 type Querier struct { 99 Keeper 100 } 101 102 func (q Querier) QueryBalance(ctx context.Context, params *types.QueryBalanceParams) (*sdk.Coin, error) { 103 balance := q.GetBalance(sdk.UnwrapSDKContext(ctx), params.Address, params.Denom) 104 return &balance, nil 105 } 106 ``` 107 108 ### Custom Query Registration and Routing 109 110 Query server implementations as above would be registered with `AppModule`s using 111 a new method `RegisterQueryService(grpc.Server)` which could be implemented simply 112 as below: 113 114 ```go 115 // x/bank/module.go 116 func (am AppModule) RegisterQueryService(server grpc.Server) { 117 types.RegisterQueryServer(server, keeper.Querier{am.keeper}) 118 } 119 ``` 120 121 Underneath the hood, a new method `RegisterService(sd *grpc.ServiceDesc, handler interface{})` 122 will be added to the existing `baseapp.QueryRouter` to add the queries to the custom 123 query routing table (with the routing method being described below). 124 The signature for this method matches the existing 125 `RegisterServer` method on the GRPC `Server` type where `handler` is the custom 126 query server implementation described above. 127 128 GRPC-like requests are routed by the service name (ex. `cosmos_sdk.x.bank.v1.Query`) 129 and method name (ex. `QueryBalance`) combined with `/`s to form a full 130 method name (ex. `/cosmos_sdk.x.bank.v1.Query/QueryBalance`). This gets translated 131 into an ABCI query as `custom/cosmos_sdk.x.bank.v1.Query/QueryBalance`. Service handlers 132 registered with `QueryRouter.RegisterService` will be routed this way. 133 134 Beyond the method name, GRPC requests carry a protobuf encoded payload, which maps naturally 135 to `RequestQuery.Data`, and receive a protobuf encoded response or error. Thus 136 there is a quite natural mapping of GRPC-like rpc methods to the existing 137 `sdk.Query` and `QueryRouter` infrastructure. 138 139 This basic specification allows us to reuse protocol buffer `service` definitions 140 for ABCI custom queries substantially reducing the need for manual decoding and 141 encoding in query methods. 142 143 ### GRPC Protocol Support 144 145 In addition to providing an ABCI query pathway, we can easily provide a GRPC 146 proxy server that routes requests in the GRPC protocol to ABCI query requests 147 under the hood. In this way, clients could use their host languages' existing 148 GRPC implementations to make direct queries against Cosmos SDK app's using 149 these `service` definitions. In order for this server to work, the `QueryRouter` 150 on `BaseApp` will need to expose the service handlers registered with 151 `QueryRouter.RegisterService` to the proxy server implementation. Nodes could 152 launch the proxy server on a separate port in the same process as the ABCI app 153 with a command-line flag. 154 155 ### REST Queries and Swagger Generation 156 157 [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) is a project that 158 translates REST calls into GRPC calls using special annotations on service 159 methods. Modules that want to expose REST queries should add `google.api.http` 160 annotations to their `rpc` methods as in this example below. 161 162 ```protobuf 163 // x/bank/types/types.proto 164 165 service Query { 166 rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { 167 option (google.api.http) = { 168 get: "/x/bank/v1/balance/{address}/{denom}" 169 }; 170 } 171 rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { 172 option (google.api.http) = { 173 get: "/x/bank/v1/balances/{address}" 174 }; 175 } 176 } 177 ``` 178 179 grpc-gateway will work direcly against the GRPC proxy described above which will 180 translate requests to ABCI queries under the hood. grpc-gateway can also 181 generate Swagger definitions automatically. 182 183 In the current implementation of REST queries, each module needs to implement 184 REST queries manually in addition to ABCI querier methods. Using the grpc-gateway 185 approach, there will be no need to generate separate REST query handlers, just 186 query servers as described above as grpc-gateway handles the translation of protobuf 187 to REST as well as Swagger definitions. 188 189 The Cosmos SDK should provide CLI commands for apps to start GRPC gateway either in 190 a separate process or the same process as the ABCI app, as well as provide a 191 command for generating grpc-gateway proxy `.proto` files and the `swagger.json` 192 file. 193 194 ### Client Usage 195 196 The gogo protobuf grpc plugin generates client interfaces in addition to server 197 interfaces. For the `Query` service defined above we would get a `QueryClient` 198 interface like: 199 200 ```go 201 type QueryClient interface { 202 QueryBalance(ctx context.Context, in *QueryBalanceParams, opts ...grpc.CallOption) (*types.Coin, error) 203 QueryAllBalances(ctx context.Context, in *QueryAllBalancesParams, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) 204 } 205 ``` 206 207 Via a small patch to gogo protobuf ([gogo/protobuf#675](https://github.com/gogo/protobuf/pull/675)) 208 we have tweaked the grpc codegen to use an interface rather than concrete type 209 for the generated client struct. This allows us to also reuse the GRPC infrastructure 210 for ABCI client queries. 211 212 1Context`will receive a new method`QueryConn`that returns a`ClientConn` 213 that routes calls to ABCI queries 214 215 Clients (such as CLI methods) will then be able to call query methods like this: 216 217 ```go 218 clientCtx := client.NewContext() 219 queryClient := types.NewQueryClient(clientCtx.QueryConn()) 220 params := &types.QueryBalanceParams{addr, denom} 221 result, err := queryClient.QueryBalance(gocontext.Background(), params) 222 ``` 223 224 ### Testing 225 226 Tests would be able to create a query client directly from keeper and `sdk.Context` 227 references using a `QueryServerTestHelper` as below: 228 229 ```go 230 queryHelper := baseapp.NewQueryServerTestHelper(ctx) 231 types.RegisterQueryServer(queryHelper, keeper.Querier{app.BankKeeper}) 232 queryClient := types.NewQueryClient(queryHelper) 233 ``` 234 235 ## Future Improvements 236 237 ## Consequences 238 239 ### Positive 240 241 * greatly simplified querier implementation (no manual encoding/decoding) 242 * easy query client generation (can use existing grpc and swagger tools) 243 * no need for REST query implementations 244 * type safe query methods (generated via grpc plugin) 245 * going forward, there will be less breakage of query methods because of the 246 backwards compatibility guarantees provided by buf 247 248 ### Negative 249 250 * all clients using the existing ABCI/REST queries will need to be refactored 251 for both the new GRPC/REST query paths as well as protobuf/proto-json encoded 252 data, but this is more or less unavoidable in the protobuf refactoring 253 254 ### Neutral 255 256 ## References