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