Msg
s and Queries
are the two primary objects handled by modules. Most of the core components defined in a module, like Msg
services, keeper
s and Query
services, exist to process message
s and queries
. {synopsis}
- Introduction to SDK Modules {prereq}
Msg
s are objects whose end-goal is to trigger state-transitions. They are wrapped in transactions, which may contain one or more of them.
When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by BaseApp
. Then, each message contained in the transaction is extracted and routed to the appropriate module via BaseApp
's MsgServiceRouter
so that it can be processed by the module's Msg
service. For a more detailed explanation of the lifecycle of a transaction, click here.
Starting from v0.40, defining Protobuf Msg
services is the recommended way to handle messages. A Protobuf Msg
service should be created for each module, typically in tx.proto
(see more info about conventions and naming). It must have an RPC service method defined for each message in the module.
See an example of a Msg
service definition from x/bank
module:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L10-L17
Each Msg
service method must have exactly one argument, which must implement the sdk.Msg
interface, and a Protobuf response. The naming convention is to call the RPC argument Msg<service-rpc-name>
and the RPC response Msg<service-rpc-name>Response
. For example:
rpc Send(MsgSend) returns (MsgSendResponse);
sdk.Msg
interface is a simplified version of the Amino LegacyMsg
interface described below with only ValidateBasic()
and GetSigners()
methods. For backwards compatibility with Amino LegacyMsg
s, existing LegacyMsg
types should be used as the request parameter for service
RPC definitions. Newer sdk.Msg
types, which only support service
definitions, should use canonical Msg...
name.
Cosmos SDK uses Protobuf definitions to generate client and server code:
MsgServer
interface defines the server API for theMsg
service and its implementation is described as part of theMsg
services documentation.- Structures are generated for all RPC request and response types.
A RegisterMsgServer
method is also generated and should be used to register the module's MsgServer
implementation in RegisterServices
method from the AppModule
interface.
In order for clients (CLI and grpc-gateway) to have these URLs registered, the SDK provides the function RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)
that should be called inside module's RegisterInterfaces
method, using the proto-generated &_Msg_serviceDesc
as *grpc.ServiceDesc
argument.
The following way of defining messages is deprecated and using Msg
services is preferred.
Amino LegacyMsg
s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message.
A LegacyMsg
is typically accompanied by a standard constructor function, that is called from one of the module's interface. message
s also need to implement the sdk.Msg
interface:
It extends proto.Message
and contains the following methods:
Route() string
: Name of the route for this message. Typically allmessage
s in a module have the same route, which is most often the module's name.Type() string
: Type of the message, used primarly in events. This should return a message-specificstring
, typically the denomination of the message itself.ValidateBasic() error
: This method is called byBaseApp
very early in the processing of themessage
(in bothCheckTx
andDeliverTx
), in order to discard obviously invalid messages.ValidateBasic
should only include stateless checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that theamount
is strictly positive for a transfer).GetSignBytes() []byte
: Return the canonical byte representation of the message. Used to generate a signature.GetSigners() []AccAddress
: Return the list of signers. The SDK will make sure that eachmessage
contained in a transaction is signed by all the signers listed in the list returned by this method.
See an example implementation of a message
from the gov
module:
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/gov/types/msgs.go#L77-L125
A query
is a request for information made by end-users of applications through an interface and processed by a full-node. A query
is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via BaseApp
's queryrouter
so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a query
, click here.
Starting from v0.40, the prefered way to define queries is by using Protobuf services. A Query
service should be created per module in query.proto
. This service lists endpoints starting with rpc
.
Here's an example of such a Query
service definition:
As proto.Message
s, generated Response
types implement by default String()
method of fmt.Stringer
.
A RegisterQueryServer
method is also generated and should be used to register the module's query server in the RegisterServices
method from the AppModule
interface.
Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific query
object defined by module developers, contrary to message
s. Instead, the SDK took the simpler approach of using a simple path
to define each query
. The path
contains the query
type and all the arguments needed in order to process it. For most module queries, the path
should look like the following:
queryCategory/queryRoute/queryType/arg1/arg2/...
where:
queryCategory
is the category of thequery
, typicallycustom
for module queries. It is used to differentiate between different kinds of queries withinBaseApp
'sQuery
method.queryRoute
is used byBaseApp
'squeryRouter
to map thequery
to its module. Usually,queryRoute
should be the name of the module.queryType
is used by the module'squerier
to map thequery
to the appropriatequerier function
within the module.args
are the actual arguments needed to process thequery
. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in theData
field of the requestreq
instead of thepath
.
The path
for each query
must be defined by the module developer in the module's command-line interface file.Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable:
- A
querier
, to process thequery
once it has been routed to the module. - Query commands in the module's CLI file, where the
path
for eachquery
is specified. query
return types. Typically defined in a filetypes/querier.go
, they specify the result type of each of the module'squeries
. These custom types must implement theString()
method offmt.Stringer
.
Store queries query directly for store keys. They use clientCtx.QueryABCI(req abci.RequestQuery)
to return the full abci.ResponseQuery
with inclusion Merkle proofs.
See following examples:
Learn about Msg
services {hide}