Msg
Services
A Protobuf Msg
service processes messages. Protobuf Msg
services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from BaseApp
during FinalizeBlock
.
Implementation of a module Msg
service​
Each module should define a Protobuf Msg
service, which will be responsible for processing requests (implementing sdk.Msg
) and returning responses.
As further described in ADR 031, this approach has the advantage of clearly specifying return types and generating server and client code.
Protobuf generates a MsgServer
interface based on the definition of Msg
service. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each transaction.Msg
. As an example, here is the generated MsgServer
interface for x/bank
, which exposes two transaction.Msg
s:
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/x/bank/types/tx.pb.go#L564-L579
When possible, the existing module's Keeper
should implement MsgServer
, otherwise a msgServer
struct that embeds the Keeper
can be created, typically in ./keeper/msg_server.go
:
https://github.com/cosmos/cosmos-sdk/blob/28fa3b8/x/bank/keeper/msg_server.go#L16-L19
msgServer
methods can retrieve the auxiliary information or services using the environment variable, it is always located in the keeper:
Environment:
https://github.com/cosmos/cosmos-sdk/blob/07151304e2ec6a185243d083f59a2d543253cb15/core/appmodule/v2/environment.go#L14-L29
Keeper Example:
https://github.com/cosmos/cosmos-sdk/blob/07151304e2ec6a185243d083f59a2d543253cb15/x/bank/keeper/keeper.go#L56-L58
transaction.Msg
processing usually follows these 3 steps:
Validation​
The message server must perform all validation required (both stateful and stateless) to make sure the message
is valid.
The signer
is charged for the gas cost of this validation.
For example, a msgServer
method for a transfer
message should check that the sending account has enough funds to actually perform the transfer.
It is recommended to implement all validation checks in a separate function that passes state values as arguments. This implementation simplifies testing. As expected, expensive validation functions charge additional gas. Example:
ValidateMsgA(msg MsgA, now Time, gm GasMeter) error {
if now.Before(msg.Expire) {
return sdkerrrors.ErrInvalidRequest.Wrap("msg expired")
}
gm.ConsumeGas(1000, "signature verification")
return signatureVerificaton(msg.Prover, msg.Data)
}
Previously, the ValidateBasic
method was used to perform simple and stateless validation checks.
This way of validating is deprecated, this means the msgServer
must perform all validation checks.
State Transition​
After the validation is successful, the msgServer
method uses the keeper
functions to access the state and perform a state transition.
Events​
Before returning, msgServer
methods generally emit one or more events by using the EventManager
held in environment
.
There are two ways to emit events, typed events using protobuf or arbitrary key & values.
Typed Events:
ctx.EventManager().EmitTypedEvent(
&group.EventABC{Key1: Value1, Key2, Value2})
Arbitrary Events:
ctx.EventManager().EmitEvent(
sdk.NewEvent(
eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module
sdk.NewAttribute(key1, value1),
sdk.NewAttribute(key2, value2),
),
)
These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click here to learn more about events.
The invoked msgServer
method returns a proto.Message
response and an error
. These return values are then wrapped into an *sdk.Result
or an error
:
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/baseapp/msg_service_router.go#L160
This method takes care of marshaling the res
parameter to protobuf and attaching any events on the EventManager()
to the sdk.Result
.
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/proto/cosmos/base/abci/v1beta1/abci.proto#L93-L113
This diagram shows a typical structure of a Protobuf Msg
service, and how the message propagates through the module.
sequenceDiagram
participant User
participant baseApp
participant router
participant handler
participant msgServer
participant keeper
participant EventManager
User->>baseApp: Transaction Type<Tx>
baseApp->>router: Route(ctx, msgRoute)
router->>handler: handler
handler->>msgServer: Msg<Tx>(Context, Msg(..))
alt addresses invalid, denominations wrong, etc.
msgServer->>handler: error
handler->>router: error
router->>baseApp: result, error code
else
msgServer->>keeper: perform action, update context
keeper->>msgServer: results, error code
msgServer->>EventManager: Emit relevant events
msgServer->>msgServer: maybe wrap results in more structure
msgServer->>handler: result, error code
handler->>router: result, error code
router->>baseApp: result, error code
end
baseApp->>User: result, error code
Telemetry​
New telemetry metrics can be created from msgServer
methods when handling messages.
This is an example from the x/auth/vesting
module:
https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/x/auth/vesting/msg_server.go#L76-L88
Telemetry adds a performance overhead to the chain. It is recommended to only use this in critical paths