Description
Summary
This issue proposes a complementary or alternative way of representing sdk.Msg
s using protobuf service
definitions. It’s just an idea, and something to think about after Stargate. We can totally throw it out if it makes no sense! But it might also provide some nice UX improvements...
Context
When we initially designed protobuf support for sdk.Msg
s it was proposed that Msg
return types be captured with a protobuf extension field, ex:
message MsgSubmitProposal
option (cosmos_proto.msg_return) = “google.protobuf.Timestamp”;
bytes delegator_address = 1;
bytes validator_address = 2;
repeated sdk.Coin amount = 3;
}
This was never adopted, however, and we currently don’t have a mechanism for specifying the return type of a Msg
.
Having a well-specified return value for Msg
s would improve client UX. For instance, in x/gov
, MsgSubmitProposal
returns the proposal ID as a big-endian uint64
. This isn’t really documented anywhere and clients would need to know the internals of the SDK to parse that value and return it to users.
Also, there may be cases where we want to use these return values programatically. For instance, #7093 proposes a method for doing inter-module Ocaps using the Msg
router. A well-defined return type would improve the developer UX for this approach.
Proposal
Protobuf service
definitions could be used instead of concrete protobuf message
types. This would have more or less the same over-the-wire representation but improve developer UX.
Ex:
package cosmos.gov;
service Msg {
rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse);
}
// Note that for backwards compatibility this uses MsgSubmitProposal as the request type instead of the more canonical MsgSubmitProposalRequest
message MsgSubmitProposal {
google.protobuf.Any content = 1;
bytes proposer = 2;
}
message MsgSubmitProposalResponse {
uint64 proposal_id;
}
Note that overloading protobuf service
definitions like this does not violate the intent of the protobuf spec which says:
If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation.
Currently, we are encoding Msg
s as Any
in Tx
s which involves packing the binary-encoded Msg
with its type URL.
The type URL for MsgSubmitProposal
is /cosmos.gov.MsgSubmitProposal
.
The fully-qualified RPC name for the SubmitProposal
RPC call above is /cosmos.gov.Msg/SubmitProposal
which is varies by a single /
character. We could also pack this type URL into an Any
with the same MsgSubmitProposal
contents and handle it like a “packed RPC” call.
With this approach, we would get an auto-generated MsgServer
interface:
type MsgServer interface {
SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error)
}
This is almost like an automatically generated Keeper
interface. It does more or less the same thing… Instead of needing to manually register handlers with handler.go
files and manually marshal return types, the MsgServer
interface would just need to be implemented and registered and that’s it.
Also, MsgClient
interfaces would be generated so theoretically some client code could send transactions simply by calling govClient.SubmitProposal(ctx, msg)
. Obviously a lot of transaction stuff would need to happen in the background… but it could be useful for client devs.
This approach could live alongside the current concrete Msg
type approach, or even replace it if we wanted. In the example above, MsgSubmitProposal
is used as the request type so that we can either send /cosmos.gov.MsgSubmitProposal
as a concrete Msg
or /cosmos.gov.Msg/SubmitProposal
as an RPC call… (Hope that’s not too confusing…)
Consequences
Pros
- communicates return type clearly
- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it
- some keeper code could be automatically generate, would improve the UX of Do Proper Ocaps for x/bank (or not): A Case Study #7093 approach (1) if we chose to adopt that
- generated client code could be useful for clients
Cons
- supporting both this and the current concrete
Msg
type approach could be confusing - using
service
definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec)
References
- Initial pre-
Any
Msg
designs: https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc
Activity