# Make a Checkers Blockchain
Make sure you have all you need before proceeding with the exercise:
In the Starport introduction section you learned how to start a brand-new blockchain. Now it is time to dive deeper and explore how you can create a blockchain to play a decentralized game of checkers.
A good start to developing a checkers blockchain is to define the ruleset of the game. There are many versions of the rules. Choose a very simple set of basic rules (opens new window) to not get lost in the rules of checkers or the proper implementation of the board state.
Use a ready-made implementation (opens new window) with the additional rule that the board is 8x8 and played on black cells. This code will not need adjustments. Copy this rules file into a
rules folder inside your module. Change its package from
rules. You can do this by command-line:
Do not focus on the GUI for the moment. You will only lay the foundation for an interface.
Now it is time to create the first object.
# The stored game object
Begin with the minimum game information needed to be kept in the storage:
- Red player. A string, the serialized address.
- Black player. A string, the serialized address.
- Game proper. A string, the game as it is serialized by the rules file.
- Player to play next. A string.
# How to store
Knowing what to store, you now have to decide how to store a game. This is important if you want your blockchain application to accommodate multiple simultaneous games. The game is identified by a unique ID.
How should you generate the ID? You cannot let players choose it themselves as this could lead to transactions failing because of an ID clash. It is better to have a counter incrementing on each new game. This is possible because the code execution happens in a single thread. You cannot rely on a large random number like a universally unique identifier (UUID) because transactions have to be verifiable in the future.
You need to keep such a counter in storage between transactions. You can keep a unique object at a singular location instead of a single counter. You can easily add relevant elements to the object as needed in the future. Designate
idValue to the counter.
You can rely on Starport's assistance:
For the counter or rather the object that contains it, call
NextGameand instruct Starport with
You need to add
--no-message. If you omit it, Starport creates an
sdk.Msgand an associated service, whose purpose is to overwrite your
NextGame.IdValuehas to be controlled/incremented by the application and not by a player sending a value of their own choosing. Starport still creates convenient getters.
You need a map because you're storing games by ID. Instruct Starport with
scaffold mapusing the
--no-messageagain? You do not want the game objects to be created or overwritten with a simple
sdk.Msg. The application instead creates and updates the objects when receiving properly crafted messages like create game or play a move.
The command added new constants:
These constants will be used as prefixes for the keys that can access objects' storage.
# Protobuf objects
Starport creates the Protobuf objects in the
proto directory before compiling them. The
NextGame object looks like this:
StoredGame object looks like this:
Both objects compile to:
These are not the only created Protobuf objects. The genesis state is also defined in Protobuf:
Which is compiled to:
You can find query objects as part of the boilerplate objects created by Starport.
NextGame might look out of place, but keep in mind Starport creates the objects according to a model. This does not prevent you from making changes later if you decide these queries are not needed:
The query objects for
StoredGame have more use to your checkers game and look like this:
# Starport's modus operandi
Starport puts the different Protobuf messages into different files depending on their use:
query.proto. For the objects related to reading the state. Starport modifies this file as you add queries. This includes the objects to query your stored elements (opens new window).
tx.proto. For the objects that relate to updating the state. As you have only defined storage elements with
--no-message, it is empty for now. The file will be modified as you add transaction-related elements like the message to create a game.
genesis.proto. For the genesis. Starport modifies this file according to how your new storage elements evolve.
stored_game.proto. Separate files created once that remain untouched by Starport after their creation. You are free to modify them but be careful with the numbering (opens new window).
Files updated by Starport include comments like:
Starport adds code right below the comments, which explains the odd numbering with the oldest members appearing lower than recent ones. But make sure to keep these comments where they are so that Starport knows where to inject code in the future. You could add your code above or below the comments. You will be fine if you keep these comments where they are.
Some files created by Starport can be updated, but you should not modify the Protobuf-compiled files
*.pb.go (opens new window) and
*.pb.gw.go (opens new window) as they are recreated on every re-run of
starport generate proto-go or equivalent.
# Files to adjust
Starport creates files that you can and should update. For example, when it comes to the default genesis values:
You can choose to start with no games or insert a number of games to start with. You will need to choose the first ID of the first game in any case, which here is set at
# Protobuf service interfaces
Beyond the created objects Starport also creates services that declare and define how to access the newly-created storage objects. Starport introduces empty service interfaces that can be filled as you add objects and messages when scaffolding a brand new module.
In your case, Starport added to
service Query how to query for your objects:
Starport separates concerns into different files in the compilation of a service. Some of which you should edit and some should be left untouched. The following was already taken care of by Starport for your checkers game:
- The query parameters (opens new window), as well as how to serialize (opens new window) and make them conform to the right Protobuf
RequestQuery(opens new window) interface.
- The primary implementation of the gRPC service.
- The implementation of all the storage setters and getters (opens new window) as extra functions in the keeper.
- The implementation of the storage getters in the keeper as they come from the gRPC server (opens new window).
# Helper functions
Your stored game stores are only strings. But you know that they represent
sdk.AccAddress or even a game from the
rules file. You are going to do operations on them. So how about adding helper functions to
Get the game
Parse the game so that it can be played with. Notice how the
Turnhas to be set by hand:
This is a good place to introduce your own errors:
# Next up
Want to continue developing your checkers blockchain? In the next section you will learn all about introducing an
sdk.Msg to create a game.