github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-031-msg-service.md (about) 1 # ADR 031: Protobuf Msg Services 2 3 ## Changelog 4 5 * 2020-10-05: Initial Draft 6 * 2021-04-21: Remove `ServiceMsg`s to follow Protobuf `Any`'s spec, see [#9063](https://github.com/cosmos/cosmos-sdk/issues/9063). 7 8 ## Status 9 10 Accepted 11 12 ## Abstract 13 14 We want to leverage protobuf `service` definitions for defining `Msg`s which will give us significant developer UX 15 improvements in terms of the code that is generated and the fact that return types will now be well defined. 16 17 ## Context 18 19 Currently `Msg` handlers in the Cosmos SDK do have return values that are placed in the `data` field of the response. 20 These return values, however, are not specified anywhere except in the golang handler code. 21 22 In early conversations [it was proposed](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc/edit) 23 that `Msg` return types be captured using a protobuf extension field, ex: 24 25 ```protobuf 26 package cosmos.gov; 27 28 message MsgSubmitProposal 29 option (cosmos_proto.msg_return) = “uint64”; 30 string delegator_address = 1; 31 string validator_address = 2; 32 repeated sdk.Coin amount = 3; 33 } 34 ``` 35 36 This was never adopted, however. 37 38 Having a well-specified return value for `Msg`s would improve client UX. For instance, 39 in `x/gov`, `MsgSubmitProposal` returns the proposal ID as a big-endian `uint64`. 40 This isn’t really documented anywhere and clients would need to know the internals 41 of the Cosmos SDK to parse that value and return it to users. 42 43 Also, there may be cases where we want to use these return values programatically. 44 For instance, https://github.com/cosmos/cosmos-sdk/issues/7093 proposes a method for 45 doing inter-module Ocaps using the `Msg` router. A well-defined return type would 46 improve the developer UX for this approach. 47 48 In addition, handler registration of `Msg` types tends to add a bit of 49 boilerplate on top of keepers and is usually done through manual type switches. 50 This isn't necessarily bad, but it does add overhead to creating modules. 51 52 ## Decision 53 54 We decide to use protobuf `service` definitions for defining `Msg`s as well as 55 the code generated by them as a replacement for `Msg` handlers. 56 57 Below we define how this will look for the `SubmitProposal` message from `x/gov` module. 58 We start with a `Msg` `service` definition: 59 60 ```protobuf 61 package cosmos.gov; 62 63 service Msg { 64 rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); 65 } 66 67 // Note that for backwards compatibility this uses MsgSubmitProposal as the request 68 // type instead of the more canonical MsgSubmitProposalRequest 69 message MsgSubmitProposal { 70 google.protobuf.Any content = 1; 71 string proposer = 2; 72 } 73 74 message MsgSubmitProposalResponse { 75 uint64 proposal_id; 76 } 77 ``` 78 79 While this is most commonly used for gRPC, overloading protobuf `service` definitions like this does not violate 80 the intent of the [protobuf spec](https://developers.google.com/protocol-buffers/docs/proto3#services) which says: 81 > If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation. 82 With this approach, we would get an auto-generated `MsgServer` interface: 83 84 In addition to clearly specifying return types, this has the benefit of generating client and server code. On the server 85 side, this is almost like an automatically generated keeper method and could maybe be used intead of keepers eventually 86 (see [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)): 87 88 ```go 89 package gov 90 91 type MsgServer interface { 92 SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) 93 } 94 ``` 95 96 On the client side, developers could take advantage of this by creating RPC implementations that encapsulate transaction 97 logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services) 98 could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s. 99 100 Each `Msg` service method should have exactly one request parameter: its corresponding `Msg` type. For example, the `Msg` service method `/cosmos.gov.v1beta1.Msg/SubmitProposal` above has exactly one request parameter, namely the `Msg` type `/cosmos.gov.v1beta1.MsgSubmitProposal`. It is important the reader understands clearly the nomenclature difference between a `Msg` service (a Protobuf service) and a `Msg` type (a Protobuf message), and the differences in their fully-qualified name. 101 102 This convention has been decided over the more canonical `Msg...Request` names mainly for backwards compatibility, but also for better readability in `TxBody.messages` (see [Encoding section](#encoding) below): transactions containing `/cosmos.gov.MsgSubmitProposal` read better than those containing `/cosmos.gov.v1beta1.MsgSubmitProposalRequest`. 103 104 One consequence of this convention is that each `Msg` type can be the request parameter of only one `Msg` service method. However, we consider this limitation a good practice in explicitness. 105 106 ### Encoding 107 108 Encoding of transactions generated with `Msg` services do not differ from current Protobuf transaction encoding as defined in [ADR-020](./adr-020-protobuf-transaction-encoding.md). We are encoding `Msg` types (which are exactly `Msg` service methods' request parameters) as `Any` in `Tx`s which involves packing the 109 binary-encoded `Msg` with its type URL. 110 111 ### Decoding 112 113 Since `Msg` types are packed into `Any`, decoding transactions messages are done by unpacking `Any`s into `Msg` types. For more information, please refer to [ADR-020](./adr-020-protobuf-transaction-encoding.md#transactions). 114 115 ### Routing 116 117 We propose to add a `msg_service_router` in BaseApp. This router is a key/value map which maps `Msg` types' `type_url`s to their corresponding `Msg` service method handler. Since there is a 1-to-1 mapping between `Msg` types and `Msg` service method, the `msg_service_router` has exactly one entry per `Msg` service method. 118 119 When a transaction is processed by BaseApp (in CheckTx or in DeliverTx), its `TxBody.messages` are decoded as `Msg`s. Each `Msg`'s `type_url` is matched against an entry in the `msg_service_router`, and the respective `Msg` service method handler is called. 120 121 For backward compatability, the old handlers are not removed yet. If BaseApp receives a legacy `Msg` with no correspoding entry in the `msg_service_router`, it will be routed via its legacy `Route()` method into the legacy handler. 122 123 ### Module Configuration 124 125 In [ADR 021](./adr-021-protobuf-query-encoding.md), we introduced a method `RegisterQueryService` 126 to `AppModule` which allows for modules to register gRPC queriers. 127 128 To register `Msg` services, we attempt a more extensible approach by converting `RegisterQueryService` 129 to a more generic `RegisterServices` method: 130 131 ```go 132 type AppModule interface { 133 RegisterServices(Configurator) 134 ... 135 } 136 137 type Configurator interface { 138 QueryServer() grpc.Server 139 MsgServer() grpc.Server 140 } 141 142 // example module: 143 func (am AppModule) RegisterServices(cfg Configurator) { 144 types.RegisterQueryServer(cfg.QueryServer(), keeper) 145 types.RegisterMsgServer(cfg.MsgServer(), keeper) 146 } 147 ``` 148 149 The `RegisterServices` method and the `Configurator` interface are intended to 150 evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) 151 and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421). 152 153 When `Msg` services are registered, the framework _should_ verify that all `Msg` types 154 implement the `sdk.Msg` interface and throw an error during initialization rather 155 than later when transactions are processed. 156 157 ### `Msg` Service Implementation 158 159 Just like query services, `Msg` service methods can retrieve the `sdk.Context` 160 from the `context.Context` parameter method using the `sdk.UnwrapSDKContext` 161 method: 162 163 ```go 164 package gov 165 166 func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitProposal) (*MsgSubmitProposalResponse, error) { 167 ctx := sdk.UnwrapSDKContext(goCtx) 168 ... 169 } 170 ``` 171 172 The `sdk.Context` should have an `EventManager` already attached by BaseApp's `msg_service_router`. 173 174 Separate handler definition is no longer needed with this approach. 175 176 ## Consequences 177 178 This design changes how a module functionality is exposed and accessed. It deprecates the existing `Handler` interface and `AppModule.Route` in favor of [Protocol Buffer Services](https://developers.google.com/protocol-buffers/docs/proto3#services) and Service Routing described above. This dramatically simplifies the code. We don't need to create handlers and keepers any more. Use of Protocol Buffer auto-generated clients clearly separates the communication interfaces between the module and a modules user. The control logic (aka handlers and keepers) is not exposed any more. A module interface can be seen as a black box accessible through a client API. It's worth to note that the client interfaces are also generated by Protocol Buffers. 179 180 This also allows us to change how we perform functional tests. Instead of mocking AppModules and Router, we will mock a client (server will stay hidden). More specifically: we will never mock `moduleA.MsgServer` in `moduleB`, but rather `moduleA.MsgClient`. One can think about it as working with external services (eg DBs, or online servers...). We assume that the transmission between clients and servers is correctly handled by generated Protocol Buffers. 181 182 Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns. 183 184 ### Pros 185 186 * communicates return type clearly 187 * manual handler registration and return type marshaling is no longer needed, just implement the interface and register it 188 * communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that 189 * generated client code could be useful for clients and tests 190 * dramatically reduces and simplifies the code 191 192 ### Cons 193 194 * using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec) 195 196 ## References 197 198 * [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122) 199 * [proto 3 Language Guide: Defining Services](https://developers.google.com/protocol-buffers/docs/proto3#services) 200 * [Initial pre-`Any` `Msg` designs](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc) 201 * [ADR 020](./adr-020-protobuf-transaction-encoding.md) 202 * [ADR 021](./adr-021-protobuf-query-encoding.md)