How to create scalable dApps and smart contracts in Ethereum with State Channels step-by-step. Part 2
This is the second part of creating scalable decentralised applications in Ethereum with State Channels. If you haven’t read the first part, you can check it here: https://medium.com/@merunasgrincalaitis/how-to-create-scalable-dapps-and-smart-contracts-in-ethereum-with-state-channels-step-by-step-48e12481fb
What we’ll be doing in this second part
In this second part we are going to set up a websockets server for real time communication, to exchange signed messages from one user to another. You can exchange them however you want, for instance you could send those messages via email since they are plain text. But the best option is to use a real-time server because of the better user experience.
Here’s an overview of the tools that we are going to use:
- Node.js as the server.
- Socket.io as the websockets tool to exchange data in real-time.
- Solidity as the smart contract language for creating the decentralised part.
- Web3.js for communicating with the blockchain in the client web application.
Remember that what we are doing here is creating a decentralised, scalable Ethereum dice game with State Channels.
A game where 2 players have to bet on a number from 1 to 6. If player 2 guess the first player’s prediction, he wins. If the second player makes a different guess, he loses and has to pay to the first player double his bet amount.
For instance if player 1 does: Bet = 0.2 ETH and call = face 3 of the dice
And player 2 does: Bet = 0.1 ETH and call = face 2 of the dice
The player 1 wins 0.2 ETH + 0.2 ETH because he is the one deciding the final result of the game. He is the one rolling the dice. So if player 2 makes a different call from player 1, he loses and player 1 wins double the bet amount.
In this second part we are going to create the game.js functionality which contains all the tools to generate signed messages with the bets of each player.
Those signed messages will be exchanged between both players until they decide to stop playing. You’ll understand what I mean later.
Here’s the index of the contents that you are going to learn in this chapter of the guide “How to create scalable decentralised applications on Ethereum with State Channels”:
- Designing the appeareance of the game page.
- Creation of the real time socket server to exchange messages.
In the last part of the series, you’ll learn how to finish the game so that both players receive the right amount of ether after an unlimited number of games.
Are you ready to discover some mind-blowing code for a new type of Ethereum applications?
1. Designing the appeareance of the game page
The first thing that we want to create is the players’ (or clients’) html file with the design of the game page:
Here’s what I did:
- First I created a new file called
game.htmlthat contains that information.
- Then I added a main div container which has the title of the game “Ethereum Dice”, a
<div class="status"></div>which is an empty div that will show notifications about what happens during the game and the code of the game inputs.
- Then you can see the
<div class="game-info"></div>which is another div that will show the game info such as the contract address, the balance of each player and the escrows used.
- Next there are 2 blocks. The first one is the input to introduce how much Ether you want to bet for the next game. If you win you get double that amount from the other player.
- The second block contains 6 images of each dice face which you can click to select your prediction:
- After that, there’s a button “Place Bet” which will generate a signed, encrypted message with the information of your bet and dice selected.
socket.io.jswhich is the client implementation of the real-time server,
contractData.jswhich is a file containing the game’s smart contract ABI and bytecode and
game.jswhich has the core functionality of the game.
You can get the
ethereum.js library from here: https://raw.githubusercontent.com/ethereumjs/browser-builds/master/dist/ethereumjs-abi/ethereumjs-abi-0.6.5.js which is from the Ethereumjs browser builds github since that library is made for node.js specifically so we need that browser version.
ethereum-util.js library from here: https://email@example.com/dist/index.min.js
socket.io.js from here: https://github.com/socketio/socket.io-client/blob/master/dist/socket.io.js
What you have to do with those links is copy all the code, create a new file with the right name and paste the code inside there. You’ll need them in your project folder for this dapp.
The css of the html app depends on your taste, you can use mine if you are in a hurry:
Here’s how the application looks like using that html and css:
As you can see it’s a pretty straightforward design. The user can clearly see what to do to play the game. Any updates will be shown in the now hidden “status” div for a better user experience.
Remember that each game consists of a dice call from player 1 and player 2. Once you have both, a new game is started unless any of the 2 players decide to finish the game. They can play any number of games as long as they have balance left from the escrow and past wins.
- To generate encrypted, signed messages with the bets and calls of each player
- To exchange messages between players using the real-time socket library
- To update the player state after each game
Start by creating an empty
game.js file. It will have the functionality of those points above.
These are the main functions that we will develop in a moment:
The functions are explained with comments as you can see. I always recomment programmers to write a small comment on top of the function to clarify what it should do before completing the function.
Which function do you think it’s the most important of those ones?
If you said “placeBet” then you are right. Because that function is the one that will receive the user input, execute the generateHash function and signMessage all in one.
The point is to create a function that will do all the calculations when the user clicks on “Place Bet”. So let’s start by creating that one:
When we place a bet we want to do these things:
- First check if the user has enough balance to bet for this game. We use a new function called
getGameBalance()which just returns the balance of the current player so if this is the player 1, it will return the balance of the player 1 and the other way around. Below you’ll see those helper functions in detail.
- Then we check if the bet in Ether that you’re trying to make, is not exceeding the balance of the other player since it’s him who will pay for the loses.
- We then check that the bet is smaller than your escrow.
- After that we want to generate a signedMessage() which is just a long string with your bet, your selected dice, your balance, a random number called nonce and a counter called sequence.
- We need the nonce to make sure the message is unique to add security to the encryption. The nonce is just a 16 length number.
- Then we generate the hash with those parameters. The
activeDicevariable is a global variable with your selected dice face. Sequence is also a global variable that starts at 1 that we need to identify each game. Since each game needs a counter to know which game we are playing.
- Then we generate a data object with all the information. This is important because it is what the server will receive. We use the function
socket.emit()to send information to the real-time socket server.
- Finally we increase the sequence.
That was quite a bit of code! What you see is the completed
placeBet() function which could be confusing. If you are creating a similar application by yourself I recommend you to start with the checks at the top, the main code after that and the final changes at the bottom.
We need the contract address, the address of both players, the socket IDs which is just an identificator of the player’s computer, the escrows, the balances, a sequence for the game counter, the signed messages with all the encrypted information, the bets, the calls which is just the dice faces selected and the nonces used.
This game object is actually being used in the server. The server will store all the game objects in an array called
games =  . The server is very important since it will work as the container of all the games facilitating the communication between players.
Continuing with what we were doing, these are the helper functions that I used inside the placeBet() function so that you know how they work. They are called helper functions because they don’t do anything by themselves, they just help other functions with key information so that they can work better:
These are pretty straightforward. They just return basic information depending on who’s playing.
After that we have to define some important variables at the start of our file, we’ll need them later for placing bets:
The game object is the same that you saw earlier but it’s empty until the players start playing games. Also the server will store that information. It will act as the main source of data.
Now it comes the important part: the signing and hashing of the information for the state channels.
State channels are based on the fact that you can’t modify encrypted information, basically you take all your parameters and you combine them together in an encrypted string that can only be read by having the original parameters.
In other words, an encrypted string that can be used to see if the parameters originally used are valid.
Let’s say that you have these parameters:
true, false, 2, 5
Those could be the ones defining a game. So when you encrypt them with a special function you get this:
As you can see it doesn’t make sense, but the data is in there. So how do you know that that string contains those variables? Btw that string is called a “hash”.
Well, you generate another encrypted string with your variables and if it gives you the exact same code, it is valid.
Is this message valid?
We encrypt these parameters that we got earlier:
Encrypt(true, false, 2, 5)
And if we get 0xlk87afkdsy2b4ky237423sdfdfaidf62343, then the message is valid and the parameters can be trusted. Note that the
Encrypt() function doesn’t exist, it’s just pseudocode so that you understand the concept more clearly. The genuine
Encrypt() function is
generateHash() in our dapp:
Using the ethereumjs library you can generate encrypted hashes. In this case I’m using a random number called nonce, a call, a bet, the balance and the sequence of the game.
That’s how state channels work. You exchange those encrypted messages off-chain for free as long as you want with the updated game information. Because they are encrypted and stored on the server, you can verify that the information is valid.
We also want another extra layer of security. We want to make sure that those hashes are coming from the right user to avoid external people from intefiering in the game.
That’s why we need to sign them. Signing a message is just putting your ethereum address on top. The address of the signer can be extracted later for verification purposes:
web3.personal.sign you generate signed messages. In this case we are signing the
hash with your main address.
You need to do this to gurantee that the hash is coming from the other player and not someone random. It’s the best way to identify information on the blockchain.
We can now place bets with the
placeBet function since we have all the required helper functions.
Finally what we need is to communicate with the server to send those messages there, then receive other messages and update the game information. I did exactly that in these functions:
So there’s a start function that initializes the server socket real-time connection and while executing
setListeners() , which is the function used to communicate with the server by emmiting and receiving messages. Here’s the breakdown:
- When you connect to the server, you emit your address and socket identification to the server so that it says “Ok, I see that you have this address and your computer has this ID, I’ll need that later”
- Each message that the server sends to the players can be read with the event listener
socket.on(messageName)the message can also include data.
- We need the
received-both-messagesevent listeners. They are basically used to update your local
gamedata and show updated information to the user.
Here’s how the entire server file looks like:
3. Creation of the real time socket server to exchange messages
The server is the most important component since it will take care of storing the updated game imformation so that the balances of both players are valid.
Let’s start by setting up the required libraries and code:
As yo can see we are importing express, body parser, the http module, socket.io, ethereumjs-abi and ethereumjs-util. We don’t really need express or body parser but I like to have them there for future updates.
Install all those extensions by going to your project folder in your terminal and executing:
npm i -S express body-parser socket.io ethereumjs-abi ethereumjs-util
http is a module from node.js itself so you don’t have to install iy. You can see that I have an array of
games that will store the games played between the 2 players. It will be required to finish the game later on.
Before setting up the listeners to receive and emit messages to the players, I want to setup a few functions that we’ll need subsequently:
Here’s the breakdown:
resetGame()function takes the game object of the current match and removes all the data of it while keeping a few key variables such as the updated balances and the socket ids.
verifyMessage()function receives 7 arguments and checks if the signedMessage that you gave him is valid or not. This is an additional security step before sending the message to the smart contract when finishing the game.
generateHash()function takes 5 arguments and generates a hash just like in the client. We need this to verify the received message by re-creating it with those parameters.
Now let’s focus on the real deal, the
start() function that contains all the listeners for interacting with the players in real time by sending and receiving messages:
Don’t panic! It’s pretty simple if you see the code in blocks. Forget what’s inside in each
socket.on() event and just check the listeners that we set up. Here’s what I did:
- First I created an async function called
- Then I created a big block with the
iovariable is just the socket.io library that we imported at the start of the file. When a user connects, this entire block gets executed. You can see the
socket.idof that user everytime.
- Then I set up several events. The first one is the disconnect event. That one just shows a message saying that the user disconnected, we don’t need to do anything special when a user goes away.
- Next we have the
setup-player-1which receives the contract address, escrow, balance and address of the first player. That information is stored in the
gameobject. Remember that the game object has the data of the current game which gets cleaned when executing the function
resetGame()to play the next one.
- Then we have the event
setup-player-2which does the same as the past event but with the player 2. The client code detects who’s the first player and who’s the second one based on the address of each user. These setup events get executed in the index page, the one we created in the Part 1 of the series. Make sure you get the updated code of the index.js file here: https://github.com/merlox/dice/blob/master/4/src/index.js
setup-gameevent gets executed by both players when they enter the
game.htmlpage, basically when they are ready to play. It is used to send the updated game object for both players so they know how much balance each one has.
- Finally we have the
signed-message-player-2. These 2 events receive the signed message along with the unencrypted variables used originally to generate that signed message. This is important to verify the validity of the signed message. If the message is valid, we know that the bet and dice face selected are correct.
That’s about it! 7 events to interact and create matches for both players. They now can play the game and exchange signed messages using the real-time socket server:
Test it by going to the project folder on your terminal and executing:
To start the server that you just created. Then open your browser and go to
localhost:4000 in my case since I changed the port used. Set up the game as you saw in part 1 and see if your
game.js works properly.
I highly recommend you to check the updated code on my github: https://github.com/merlox/dice in the
That’s it about part 2! If you came this far make sure you reward yourself with something sweet and healthy. Be sure to check out the next and final part 3 next week where we’ll modify the smart contract to add finishing functionality and update our server with client code.
Be the first to read it when it’s completed. Join my exclusive mailing list of Ethereum Developers to receive updates and information directly from me here: http://eepurl.com/dDQ2yX
If you feel overwhelmed with such advanced information or you are just new to solidity and Ethereum dapps, check my book “Ethereum Developer: Learn Solidity From Scratch” here https://merunas.io/