It is recommended to take a look at the following previous sections to better understand messages:
In this section, you will take a closer look at messages, Msg
. At the end of the section, you can find a code example that illustrates message creation and the inclusion of messages in transactions for your checkers blockchain.
Understanding Msg
will help you prepare for the next section, on modules in the Cosmos SDK, as messages are a primary object handled by modules.
Messages are one of two primary objects handled by a module in the Cosmos SDK. The other primary object handled by modules is queries. While messages inform the state and have the potential to alter it, queries inspect the module state and are always read-only.
In the Cosmos SDK, a transaction contains one or more messages. Modules process the messages after the transaction is included in a block by the consensus layer.
Remember from the previous section on transactions that transactions must be signed before a validator includes them in a block. Every message in a transaction must be signed by the addresses as specified by GetSigners
.
The Cosmos SDK currently allows signing transactions with either SIGN_MODE_DIRECT
or SIGN_MODE_LEGACY_AMINO_JSON
methods.
When an account signs a message it signs an array of bytes. This array of bytes is the outcome of serializing the message. For the signature to be verifiable at a later date, this conversion needs to be deterministic. For this reason, you define a canonical bytes-representation of the message, typically with the parameters ordered alphabetically.
Transactions containing one or more valid messages are serialized and confirmed by CometBFT. As you might recall, CometBFT is agnostic to the transaction interpretation and has absolute finality. When a transaction is included in a block, it is confirmed and finalized with no possibility of chain re-organization or cancellation.
The confirmed transaction is relayed to the Cosmos SDK application for interpretation. Each message is routed to the appropriate module via BaseApp
using MsgServiceRouter
. BaseApp
decodes each message contained in the transaction. Each module has its own MsgService
that processes each received message.
MsgService
Although it is technically feasible to proceed to create a novel MsgService
, the recommended approach is to define a Protobuf Msg
service. Each module has exactly one Protobuf Msg
service defined in tx.proto
and there is an RPC service method for each message type in the module. The Protobuf message service implicitly defines the interface layer of the state, mutating processes contained within the module.
How does all of this translate into code? Here is an example MsgService
from the bank
module (opens new window):
In this example:
Msg
service method has exactly one argument, such as MsgSend
, which must implement the sdk.Msg
interface and a Protobuf response.Msg<service-rpc-name>
and the RPC response Msg<service-rpc-name>Response
.The Cosmos SDK uses Protobuf definitions to generate client and server code:
MsgServer
interface defines the server API for the Msg
service. Its implementation is described in the Msg
services documentation (opens new window).If you want to dive deeper when it comes to messages, the Msg
service, and modules, see:
Msg
service (opens new window).Msg
services - Amino LegacyMsg
(opens new window).For the command-line interface (CLI), module developers create subcommands to add as children to the module-level message commands. These commands describe how to craft a message for inclusion in a transaction.
With v0.50, the Cosmos SDK introduces the autocli
facility (opens new window). This takes care of a lot of the boilerplate and lets you define the available CLI commands in a descriptive manner.
In the previous design exercise's code examples, the ABCI application was aware of a single transaction type: that of a checkers move with four int
values. With multiple games, this is no longer sufficient. Additionally, you need to conform to the SDK's way of handling Tx
, which means creating messages that are then included in a transaction.
If you want the guided coding exercise instead of design and implementation considerations, see the links at the bottom of the page.
What you need
Begin by describing the messages you need for your checkers application to have a solid starting point before diving into the code:
sdk.Msg
, wrapped in said transaction. Four flat int
values are no longer sufficient, as you need to follow the sdk.Msg
interface, identify the game for which a move is meant, and distinguish a move message from other message types.How to proceed
Focus on the messages around the game creation. There is no single true way of deciding what goes into your messages. The following is one reasonable example.
The message itself is structured like this:
Note that Creator
contains the address of the message signer.
The corresponding response message would then be:
The idea here is that when the creator does not know the ID of the game that will be created, the ID needs to be returned.
With the messages defined, you need to declare how the message should be handled. This involves:
How to proceed with v0.50 or after
The SDK takes care of a lot of things. In particular, by annotating your Protobuf message, you can have it implement sdk.Msg
after compilation. For instance:
Then, you declare a Protobuf message server:
After compilation, you register this together with your interface registry so that the module knows to send any MsgCreateGame
to the CreateGame
function of the MsgClient
interface.
You also define a local msgServer
that implements the CreateGame
function with the required actions, such as:
How to proceed with v0.47 or earlier and Ignite
Thinking from design to implementation, Ignite CLI can help you create these elements, plus the MsgCreateGame
and MsgCreateGameResponse
objects, with this command:
Ignite CLI creates a variety of other files. See Run Your Own Cosmos Chain for details, and to make additions to existing files.
A sample of things Ignite CLI does for you
Ignite CLI significantly reduces the amount of work a developer has to do to build an application with the Cosmos SDK. Among others, it:
sdk.Msg
by adding the boilerplate for GetSigners
and GetSignBytes
.// TODO: Handling the message
where you should place your code.Ignite CLI is opinionated in terms of which files it creates to separate which concerns. If you are not using it, you are free to create the files you want.
What is left to do?
Your work is mostly done. You want to create the specific game creation code to replace // TODO: Handling the message
. For this, you need to:
Decide how to create a new and unique game ID: newIndex
.
For more details, and to avoid diving too deep in this section, see:
Extract and verify addresses from the message.
Create a game object with all required parameters and save it to storage
And finally, return the expected message.
Remember, as a part of good design practice:
panic("This situation should not happen")
.error
.Other considerations
What would happen if one of the two players has accepted the game by playing, but the other player has neither accepted nor rejected the game? You can address this scenario by:
What would happen if a player stops taking turns? To ensure functionality for your checkers application, you can consider:
EndBlock
(or rather its equivalent in the Cosmos SDK) without any of the players having to trigger the cancelation. For the guided coding exercise on this part, head straight to Auto-Expiring Games.In general terms, you could add timeout: Timestamp
to your StoredGame
and update it every time something changes in the game. You can decide on a maximum delay, for example one day.
There are no open challenges, meaning a player cannot create a game where the second player is unknown until someone steps in. Therefore, player matching is left outside of the blockchain. The enterprising student can incorporate it inside the blockchain by changing the necessary models.
If you would like to get started on building your own checkers game, you can go straight to the main exercise in Run Your Own Cosmos Chain, either natively with SDK v0.50 or with Ignite CLI to start from scratch.
More specifically, you can jump to:
MsgCreateGame
message natively with SDK v0.50.MsgCreateGame
with Ignite CLI.MsgCreateGame
created with Ignite CLI.MsgPlayMove
, still with Ignite CLI.To summarize, this section has explored: