Run Ethereum DApp Game Contract


Run Ethereum DApp Game Contract

Recipe ID: hsts-r42


Self-paced training

We offer blockchain introduction, Hyperledger for system admin, Ethereum, Solidity, Corda R3, Hyperledger for developers, blockchain cybersecurity and more classes in self-paced video format starting at $60. Click here to learn more and register. For complete self-paced blockchain training, visit our Complete Blockchain Development Training page.


Update on Feb 2021

The latest version of this tutorial along with all updated source codes is available via the below link:
https://myhsts.org/blog/ethereum-dapp-with-evm-remix-golang-truffle-and-solidity-part2.html

Recipe Overview

Ethereum is a general-purpose blockchain that is more suited to describing business logic, through advanced scripts, also known as smart contracts. Ethereum was designed with a broader vision, as a decentralized or world computer that attempts to marry the power of the blockchain, as a trust machine, with a Turing-complete contract engine. Although Ethereum borrows many ideas that were initially introduced by bitcoin, there are many divergences between the two.
The Ethereum virtual machine and smart contracts are key elements of Ethereum, and constitute its main attraction. In Ethereum, smart contracts represent a piece of code written in a high-level language (Solidity, LLL, Viper) and stored as bytecode in the blockchain, in order to run reliably in a stack-based virtual machine (Ethereum Virtual Machine), in each node, once invoked. The interactions with smart contract functions happen through transactions on the blockchain network, with their payloads being executed in the Ethereum virtual machine, and the shared blockchain state being updated accordingly.

For those who are not familiar with blockchain technology reading History and Evolution of Blockchain Technology from Bitcoin article is strongly recommended. Also, if you wish to learn and practice Hyperledger blockchain development, visit Comprehensive Hyperledger Training Tutorials page to get the outline of our Hyperledger tutorial articles.
We have written two sets of tutorials to explore Ethereum and Solidity programming in depth. First set covered the following nine recipes:

In short, you learned about how to set up and configure Ethereum and develop blockchain applications using Solidity programming language. We explored its key components, including smart contracts and Web3.JS API via an Auction Decentralized Application (DApp) step-by-step.
In second set, we discuss more advance topics in Ethereum blockchain development and solidity while building a Tontine DApp game step-by-step. Specifically, we cover Truffle and Drizzle. For instance, we show you how a tool such as Truffle can be an assistant in building, testing, debugging, and deploying DApps. In summary, we are going to cover four main topics:

The 2nd set consists of 8 recipes as follows:

IMPORTANT: Understanding and completing the first set of recipes are required prior to working on second set of recipes.
In our first round of recipes, we learned a lot about the Ethereum ecosystem, but we are yet to realize the full potential of its different components. More precisely, we explored how Ethereum works, what a decentralized application (DApp) is and how to build one, and also covered the key concepts of Solidity and web3.js. We then introduced some of the most common smart contract design patterns (withdrawal from a contract, restricting access, state machines), before ending with a discussion of a contract’s cost optimization.
To brush up your knowledge and skills, on second round of recipes, we are going to build a Tontine DApp game. We will exploit this example to explore new tools that are going to change the way you build DApps, and introduce new Solidity features.

In this walkthrough, we will discover how a tool such as Truffle can aid in building, testing, debugging, and deploying our DApp.

As stated in the previous recipe, Tontine is a competitive multiplayer game. When it starts, players pool their money into the Tontine contract, and once the game is over, all the funds are handed to the last surviving participant. Meanwhile, players take turns trying to stabilize their position and eliminate their opponents. The main rules of our Tontine game are as follows:

 

General structure

Technically speaking, unlike a traditional online game, which has all of its business logic defined and executed on a private server owned by a company, our game's business logic is defined and executed in a smart contract on the decentralized Ethereum blockchain.

In our design, we will define two main contracts with a single interface. This wouldn't be the best approach out there, but I designed it as such to make it easier to discover new Solidity features, such as interfaces, contract interactions, and overloading. It's worth mentioning that for the purpose of removing complexities and to help learn advanced Solidity features, optimization and security are intentionally not considered.

We will separate the function declarations from their definitions by using an interface instead of an abstract contract. Obviously, you don't have to create an interface or abstract class for each contract you build, but we are adopting this pattern for learning purposes to show you that such a well-known pattern in Object-Oriented Programming is applicable in Solidity as well.


UML model

For building this game, we will adopt the following straightforward UML diagram:
Truffle and Ganache for Ethereum Smart Contract management

So far, we have three main components:

 

As you'll notice, the interface name starts with an I prefix, whereas contracts starts with a C
prefix.


 

Preparing the contracts

All our code will be hosted in a single Solidity file, so first create an empty file called
tontine.sol in Truffle’s contracts/ folder using touch contracts/tontine.sol.

 

Cplayer as a CRUD contract

We'll start by building the Cplayer contract, which is responsible for managing player accounts. Going forward, we will use Solidity to build it as a CRUD smart contract.

As you know, the CRUD paradigm is common in constructing web applications as its operations generally constitute the fundamental structure. In fact, the CRUD acronym's create, read, update and delete operations represent the basic operation we'll need to perform on the data repository (Smart contract storage) while managing the players.

At this point, to build a CRUD contract, we first need to understand where and how data is stored and accessed in Ethereum's blockchain.

 

Smart contract data location

Although each smart contract has its own separate storage space, we do not decouple the storage-layer code from the other layers of the application as we do in classical development methods. Therefore, there is no querying language, such as SQL, or separated database component, but the smart contract through its code provides the ability to initiate permanent states, and read or store data.

Truffle and Ganache for Ethereum Smart Contract management

The smart contract is executed in the EVM to update the system state. This virtual machine provides dedicated storage, which is a persistent associative map where the contract stores states (permanent data). It also provides volatile memory to hold transient data while a contract is being executed. On the other hand, the contract's bytecode (encoded sequence of opcodes) is stored separately, in a virtual ROM. The following figure illustrates a simplified architecture of the EVM and its interactions with the blockchain:

We learned in the previous recipes, Peer-to-Peer Auctions in Ethereum that a contract has state variables (we call them the contract's state) similar to global variables (defined in the contract scope) that are located by default in permanent storage.

There are also default locations depending on which type of variable it concerns:

 

The following picture presents the default locations for different elements in a smart contract (storage in red, memory in green) :
Truffle and Ganache for Ethereum Smart Contract management

Depending on the context, you can use dedicated memory or storage keywords to change the data location. These locations are important to determine how an assignment is performed. In general, assignments between storage and memory variables, and also to a state variable, are passed by value. However, assignments to local storage variables (arrays or structs declared within the function scope) pass the state's value by reference.

 

The CRUD data repository

As our main data repository, we will use a mapping structure to index all enrolled players using their address. If you remember, in the previous recipes, Peer-to-Peer Auctions in Ethereum we learned that a mapping is similar to a big key-value database with all possible key values filled with zeros.

In the tontine.sol file, add the following code:
pragma solidity ^0.4.24;

contract Cplayer{ struct player{
string name;
uint PhoneNumber; address Paddress; uint id;
}
mapping(address => player) players;
}

The code is pretty self-explanatory: we declare a new type called player, which is a structure (struct) that represents a user’s details.
We also declare a players mapping to store the users' details. In the mapping definition (key ≥ value), the key (which acts like a unique ID) can be almost any type, except for a mapping, a dynamically sized array, a contract, an enum, or a struct. The corresponding value can actually be any type, including a mapping.
It’s magic that the data storage is fully managed by a single line of code. In our case, declaring a mapping in the code is enough to store players. We don’t have to create a database or table or install a database driver.
To give you an idea, the previous Solidity code is similar to creating a table in SQL:

CREATE TABLE players (`address` varchar(32) NOT NULL, PRIMARY KEY (`address`) …);

Generally, in a standard RDBMS, you define a unique key as an incremental integer, whereas for a mapping, the uniqueness is ensured by the collision-resistance property of sha256 used to manage the keys, therefore two different keys can’t have the same hash.

 

CRUD – Create

Great, now that our storage is initiated, let’s define the first CRUD operation.


The CREATE operation represents adding new entries to our persistent storage structure (player mapping). To insert a new player, we will define an addPlayer method, as follows:
function addPlayer(string _name, uint256 _phonenumber) public returns (bool) {
players[msg.sender].name = _name; players[msg.sender].Paddress = msg.sender; players[msg.sender].PhoneNumber = _phonenumber; return true;
}

This function enables a new player to enroll themselves in the players, database. Similar to an INSERT statement, the different assignments will create (or, more accurately, fill) a new record in the mapping, indexed using the player address. By analogy, in SQL we can do the same thing using insert into players values (_name, _msg. sender,
_phonenumber);.

 

CRUD – Read

In the CRUD pattern, the READ operation expresses the action of retrieving, searching, or viewing existing entries. Correspondingly, we define the findPlayer method in the Cplayer contract to read the mapping records based on the primary key (player address) specified as an argument:

function findPlayer(address _address) public view returns (string, uint, address) {
return (players[_address].name, players[_address].PhoneNumber, players[_address].Paddress);
}

This simple, magical function will directly access a player's details, by providing only its address without the need of looping through the mapping. Moreover, as you may have noticed, Solidity enables us to return multiple values for a function.

Similarly, if we suppose that the players mapping is a table with three columns, then the findPlayer method will be similar to performing the following SQL request: Select * from players where Paddress=_address;.
Remember, you can define the mapping as public to retrieve the player information directly without using a find method.

Furthermore, we can check the existence of an element in the mapping by defining the following function:

function exist(address _address) public view returns (bool) { return (players[_address].Paddress != 0x0);
}

The preceding method will read and check whether the player address is not null (not zero), as mappings are initialized to zero for each possible key/value. In Solidity, the null statement is not defined.

Furthermore, to retrieve a player as a compact structure object, instead of returning a tuple of values, we use the following method:

function getplayer(address _adress) public view returns (player) { return players[_adress];
}

However, Solidity, at the time of writing this recipe, doesn’t allow us to return struct objects from a function unless you are using a compiler newer than 0.4.16. In this case, you will need to activate the new ABI decoder, which is still an experimental feature, by adding the pragma experimental ABIEncoderV2; directive.
Why's that? Because Solidity doesn't recognize structs as real objects, therefore standard ABI doesn't support them.

At the time of writing, passing structs between contracts isn't a good idea. Nevertheless, I have chosen this pattern knowing that in the future, Solidity compilers will provide full support for this feature. You can instead use an unpacker method to return the struct elements, such as findPlayer. If you plan to deploy your contract to the Mainnet, think of removing the experimental features, such as ABIEncoderV2, to avoid any malfunctioning.

 

Mapping iterations

While we are talking about search logic, let me open a parenthesis about mappings to answer a recurring question.

Are mappings iterable? The answer is no, they aren’t.

Currently Solidity doesn’t support iterating a mapping, as they don’t have a fixed length (number of non-null key-values entries). However, you can use an auxiliary array and structure in which you store the keys to your mapping to tie each element to its key. Using this pattern, the IterableMapping library was built by Solidity developers to iterate over the mapping keys. Its code is available in the following GitHub repository: https:// github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol.

 

CRUD – Update

Next up, we’re going to perform entry updates. As its name suggests, the update operation in CRUD performs an update or edit to existing entries in our players' mapping storage.
Our update method will be editPlayer() , which enables the players themselves to edit
their own details by sending a transaction to the Cplayer contract. Here’s the code for
editPlayer() and the logic for editing individual contacts:
function editPlayer(string _name, uint256 _phonenumber, address
_address,uint256 _id) public returns (bool) { players[msg.sender].name = _name; players[msg.sender].PhoneNumber = _phonenumber; players[msg.sender].Paddress = _address; players[msg.sender].id = _id;
return true;
}

As every user has their details mapped to their address (ID), we need only to target the ID in the mapping and update the attribute of the corresponding struct.

Again, let's make a point about immutability in Ethereum.

Every time you update a new entry, you don’t erase or override the old value. When you update a contract's state, you define a new value that will be included in the current block (on the blockchain), but the old value will still be living in a previous block. This is similar to Lavoisier's law: nothing is lost, everything is transformed.
In SQL, we can do the same thing using an UPDATE statement on the players, table based on the primary key for a record specified in the WHERE clause:
UPDATE players
SET name = name,... WHERE ID = msg.sender;


However, unlike in SQL, you can’t alter an existing mapping as you do for tables. The code is definitively immutable.

 

Function overloading

As for other languages, Solidity has the ability to define different semantics for a function using different types and numbers of arguments. The following example shows the overloading of the editPlayer function:
function editPlayer(address _address,uint256 _id) public returns (bool) { players[_address].id = _id;
return true;
}

This is essentially the same as the EditPlayer function we defined before, the difference being it will edit only the player's ID attribute. This overloaded function will be executed if EditPlayer is called with two arguments (string and uint256).

 

CRUD – Delete

So far, we have implemented three of the main CRUD operations. Now it’s time to define the last, the DELETE operation, to deactivate or remove existing entries. To achieve that, we define the following removePlayer() method:
modifier onlyadmin(){ require(msg.sender == admin);
_;
}

function removePlayer(address _address) public onlyadmin returns (bool) { delete players[_address];
return true;
}

removePlayer() will remove an element (player) at a specific index (address). Solidity provides the delete operator to reset deleted elements to the default value. In the mapping, the corresponding value to the specified key (address) will be set to zero, and the mapping structure will remain the same.

Moreover, we can apply delete to a given array member as Delete MyArray[3]. This implies that the value of MyArray[3] will equal zero without reducing the array length. If we want to physically remove the element with reindexing, we need to delete it and manually resize the array. Deleting elements in Solidity behaves like an update with the initial value for the type.

The equivalent SQL statement to the previous function would be DELETE from players where address=_address;.
You might be wondering about deleting the mapping as we do in SQL for a table: DROP TABLE players;
This is currently impossible for a mapping, but it's possible to delete a dynamic array or a structure.

One last thing to mention: in your CRUD contract, you can manage contract roles and permissions using modifiers. For example, we can define fixed roles and assign permissions based on specific attributes (owner, and player) to control access to specific functions (actions).

At this point, we have accomplished the goal of this first section. We have built our Cplayer contract following a CRUD pattern. Now, let's move on to the next recipe by building the Ctontine contract while creating an Itontine interface.

To conclude this recipe, we like to recommend our Learn Hands-on Blockchain Ethereum Development & Get Certified in 30 Hrs, and Become Blockchain Certified Security Architect in 30 hours courses to those interested in pursuing a blockchain development career. This recipe is written by Brian Wu who is our senior Blockchain instructor in Washington DC. His Blockchain By Example book is highly recommended for learning more about blockchain development.

Related Training Courses

Hands-on Node.JS, MongoDB and Express.js Training
Advance JavaScript, jQuery Using JSON and Ajax
Learn Hands-on Blockchain Ethereum Development & Get Certified in 30 Hrs
Learn Blockchain Hyperledger Development & Get Certified in 30 Hrs
Become Blockchain Certified Security Architect in 30 hours
Blockchain Certified Solution Architect in 30 hours
Introduction to Python Programming
Object Oriented Programming with UML


Private and Custom Tutoring

We provide private tutoring classes online and offline (at our DC site or your preferred location) with custom curriculum for almost all of our classes for $50 per hour online or $75 per hour in DC. Give us a call or submit our private tutoring registration form to discuss your needs.


View Other Classes!