# Messages and Queries

Msgs and Queries are the two primary objects handled by modules. Most of the core components defined in a module, like Msg services, keepers and Query services, exist to process messages and queries.

# Pre-requisite Readings

# Messages

Msgs 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.

# Msg Services

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:

Copy // Msg defines the bank Msg service. service Msg { // Send defines a method for sending coins from one account to another account. rpc Send(MsgSend) returns (MsgSendResponse); // MultiSend defines a method for sending coins from some accounts to other accounts. rpc MultiSend(MsgMultiSend) returns (MsgMultiSendResponse); }

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:

Copy 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 LegacyMsgs, 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 the Msg service and its implementation is described as part of the Msg 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.

# Legacy Amino LegacyMsgs

The following way of defining messages is deprecated and using Msg services is preferred.

Amino LegacyMsgs 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. messages also need to implement the sdk.Msg interface:

Copy // Msg defines the interface a transaction message must fulfill. Msg interface { proto.Message // Return the message type. // Must be alphanumeric or empty. Route() string // Returns a human-readable string for the message, intended for utilization // within tags Type() string // ValidateBasic does a simple validation check that // doesn't require access to any other information. ValidateBasic() error // Get the canonical byte representation of the Msg. GetSignBytes() []byte // Signers returns the addrs of signers that must sign. // CONTRACT: All signatures must be present to be valid. // CONTRACT: Returns addrs in some deterministic order. GetSigners() []AccAddress }

It extends proto.Message and contains the following methods:

  • Route() string: Name of the route for this message. Typically all messages 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-specific string, typically the denomination of the message itself.
  • ValidateBasic() error.
  • 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 each message 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:

Copy // Route implements Msg func (m MsgSubmitProposal) Route() string { return RouterKey } // Type implements Msg func (m MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } // ValidateBasic implements Msg func (m MsgSubmitProposal) ValidateBasic() error { if m.Proposer == "" { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Proposer) } if !m.InitialDeposit.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String()) } if m.InitialDeposit.IsAnyNegative() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String()) } content := m.GetContent() if content == nil { return sdkerrors.Wrap(ErrInvalidProposalContent, "missing content") } if !IsValidProposalType(content.ProposalType()) { return sdkerrors.Wrap(ErrInvalidProposalType, content.ProposalType()) } if err := content.ValidateBasic(); err != nil { return err } return nil } // GetSignBytes implements Msg func (m MsgSubmitProposal) GetSignBytes() []byte { bz := ModuleCdc.MustMarshalJSON(&m) return sdk.MustSortJSON(bz) } // GetSigners implements Msg func (m MsgSubmitProposal) GetSigners() []sdk.AccAddress { proposer, _ := sdk.AccAddressFromBech32(m.Proposer) return []sdk.AccAddress{proposer} } // String implements the Stringer interface func (m MsgSubmitProposal) String() string { out, _ := yaml.Marshal(m) return string(out) }

# Queries

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.

# gRPC Queries

Starting from v0.40, the prefered way to define queries is by using Protobuf services (opens new window). 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:

Copy // Query defines the gRPC querier service. service Query { // Account returns account details based on address. rpc Account(QueryAccountRequest) returns (QueryAccountResponse) { option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; } // Params queries all parameters. rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/cosmos/auth/v1beta1/params"; } }

As proto.Messages, generated Response types implement by default String() method of fmt.Stringer (opens new window).

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.

# Legacy Queries

Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific query object defined by module developers, contrary to messages. 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:

Copy queryCategory/queryRoute/queryType/arg1/arg2/...

where:

  • queryCategory is the category of the query, typically custom for module queries. It is used to differentiate between different kinds of queries within BaseApp's Query method.
  • queryRoute is used by BaseApp's queryRouter to map the query to its module. Usually, queryRoute should be the name of the module.
  • queryType is used by the module's querier to map the query to the appropriate querier function within the module.
  • args are the actual arguments needed to process the query. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the Data field of the request req instead of the path.

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 the query once it has been routed to the module.
  • Query commands in the module's CLI file, where the path for each query is specified.
  • query return types. Typically defined in a file types/querier.go, they specify the result type of each of the module's queries. These custom types must implement the String() method of fmt.Stringer (opens new window).

# Store Queries

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:

Copy req := abci.RequestQuery{ Path: fmt.Sprintf("store/%s/key", host.StoreKey), Height: height, Data: key, Prove: true, } res, err := clientCtx.QueryABCI(req) if err != nil { return nil, nil, clienttypes.Height{}, err }

Copy func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { // "/store" prefix for store queries queryable, ok := app.cms.(sdk.Queryable) if !ok { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries")) } req.Path = "/" + strings.Join(path[1:], "/") // when a client did not provide a query height, manually inject the latest if req.Height == 0 { req.Height = app.LastBlockHeight() } if req.Height <= 1 && req.Prove { return sdkerrors.QueryResult( sdkerrors.Wrap( sdkerrors.ErrInvalidRequest, "cannot query with proof when height <= 1; please provide a valid height", ), ) } resp := queryable.Query(req) resp.Height = req.Height return resp }

# Next

Learn about Msg services