Building Cosmos (Part 2/3): Messages and Query Commands

Stoyan Dimitrov
6 min readJan 7, 2022

--

Photo by Bill Jelen on Unsplash

In part one of this tutorial we built our own blockchain on top of the cosmos-sdk. You’ll need to read that before continuing on here, as it sets up the project we’ll be working on in this article.

Our game logic and most of our coding will be placed in a module in the “/x/button” directory. Let’s start by creating two new folders to hold our module called “/x/button” in our root directory. Now let’s get to the exciting part and write our game!

Transaction Messages

The first step is to define the actions that a player can take in our game. Each action will be defined by a message. In our case, we want the players to be able to press the button and to claim the treasure. To do this we will create a separate message for each, add msg_press_button.go and mgs_claim_treasure.go to the button directory, and copy the code below into each of them.

x/button/msg_press_button.go

We can see that both of the messages follow a similar structure and are very easy to implement, with each message having a struct that can be decoded from JSON. The ‘Route’ method is used to direct the app router to the handler that should process this message. The ‘Type’ method is used for indexing with tags any message that is processed. ‘ValidateBasic’ performs a basic sanity check that the message fields are set. ‘GetSignedBytes’ and ‘GetSigners’ are used by the auth module to authenticate that the ‘sender’ in our message has actually signed the message (this will be included in our project later).

The ‘sender’ is a required parameter that all messages have, since it’s used to authenticate the message. The messages are pretty straightforward to implement. Most of the work for them is just deciding what fields and values they need to have. Now that we have defined our press_button and claim_treasure messages, we can move on to the codec. The codec is there to decode our messages when we receive them.

Since the tendermint node sends our app encoded bytes to process, we first need a codec that can decode the bytes into a message we can read. The particular encoding that tendermint uses is called amino, and is based on google’s protobuf3 (you can find more about protobufs here). To register our messages with the codec, we need to create a codec.go file in our ‘x/button/’ directory. This file will hold all of the concrete struct and interfaces that we need to decode. Since we only have two message types that are both implemented as concrete structs, our codec is short and sweet. All it takes to register each of our messages is a call to ‘cdc.RegisterConcrete’, to which we pass the struct of the message, along with the message “name”. This name is actually the type for this message that will be used to match the message.

codec.go

We have defined the messages that we want our players to make, and registered them with our codec. Next we need to update the codec that our app will use to process messages with ours. Go ahead and add our new codec to the app.go file:

We added our codec and messages to the app, and now we will create the commands that a user can call to make these message calls. In our app there are two types of calls that the user can make: one type is to query the chain for the state, and the other is to make transactions on chain. We will refer to these two types as query and transaction commands. We will start with the query commands since they don’t need to be signed by the caller, as they are just look ups of state that transfer no funds.

Fig. 1 Path of a query sent to our app

We can see from Fig 1 the path a query will take in our app. A query will be initiated by the btcli tool, which will then be routed by our app to our querier. The querier’s job is to look at the chain state in the keeper, package the result, and then return the response to the caller. We will go over how the keeper works later, but in short it is the on-chain store of data that we use to read and write to the blockchain. Let’s create the querier file now, call it ‘querier.go’ and place it in ‘/x/button’.

Once the user makes the query, the sdk will forward it based on its route to our querier. In the querier we split the message route and then redirect it to a method that will respond to the request. Now we’re ready to link our game’s querier to the app. Since all queries to the app go through their own query router we’ll need to add an instance of ‘Querier’ to app.go as well as our placeholder Keeper to the ButtonApp structure:

We are going to create the query commands first, and then we will add them into our btcli tool that we can use to play the game from a terminal. First, let’s create a file called ‘query.go’ in ‘/x/button/cli’. This file will contain all of the query calls we will expose to the players of the game. We are only interested in getting the balance in the prize pool, and how long ago the last button click was.

x/button/cli/query.go

We use the cobra library to create the subcommands. The interesting part for us is the ‘cliCtx.QueryWithData’ call, which is where we’re making a blockchain lookup. We pass it a route that begins with the keyword ‘custom’, which tells the sdk to route that call with its data to a querier that we have previously set. We then pass the route path that we will then parse in our querier to know how to handle each request. Both our commands use the context to query the chain, and then print out the results.

Since there are several commands that we’ll add to our btcli tool, we are going to create a structure to group them together under the same command root. Cosmos already has an interface defined for this, which helps make it easier to add query and transaction commands to our btcli tool. Create a file named ‘button_cli.go’ in ‘/x/button/cli’ and copy the code below.

We left the ‘GetTxCmd()’ empty, but included it because the cosmos-sdk interface requires that function to be defined. The last thing to do before we can test that our users can make queries to our game is to add them to our btcli tool. Add the lines below to our ‘cmd/btcli/main.go’ file.

Add query commands to /cmd/btcli/main.go

And that’s it! We should now be able to compile and run our app with our new messages providing the ability to make requests to check the state of our game. Your code should now look like our branch: 3_messages.

If you missed any of the code above, you can just check out the current branch by running:

Let’s now compile and make some requests to our chain.

Once our chain is up, check that our btcli tool now has the button sub-command in query.

Now we can check that our two message queries are there under button:

We can call our prize and age commands, with the flag ‘ — trust-node’ to see that our queries are making their way to our querier and back to the terminal.

If you are seeing the above “Query not implemented” message, then everything is working. Let’s quickly review the steps that got us here so quickly:

  1. We were able to create messages to query our chain.
  2. We made a codec and querier that we attached to our chain on startup so that we could process and route queries made by our players.
  3. Finally, we added commands to our btcli tool so that we could have an interface for our players to use.

In the next part of the tutorial we will finish by adding the on-chain storage and game logic to our chain, and then play a full game!

--

--