How smart contracts enable best security for game scores

Recent trend in technology is blockchain. Since the first contact with smart contracts, there has always been one question: “NICE, but now, for what?”
On the one hand, the benefits of blockchains like tamper-proof and transparency can be very helpful. But a database would be cheaper and the existing knowledge is more advanced.

Leave the obvious use cases (e.g. lending, land register, identification) of the FINTECHs behind, which other applications could enjoy these benefits in the future? Recent innovations like virtual reality had their debut in the gaming sector. And now, this segment can profit also from the blockchain.

Idea

A standard blockchain is not suitable for interactive games due to its latency and trading of game items or virtual goods is already there in many distributed Apps (DApp). So I am focusing on the end of the gaming chain: the results or leaderboards.

Using a smart contract on the Ethereum blockchain to host decentralized leaderboards for tracking scores, rankings and multi signature security

Challenges

Usability

The drawback for the adoption of blockchains is the deficient usability. It’s interactions are not that straight forward. Terms like wallets, private keys and DApp Browsers are scaring off users who are blessed with simplicity like simple-UIs and single-logons. 
The first prototype of BlockScores started with a non-wallet-version. It had all transactions signed from a single address with a password hardcoded. But this was only usable on a local testnet for sure. 
Under real conditions, web3 injectors like Metamask, Toshi, MIST or Cipher (iOS; incl. testnet) are the only way to go. Unless you do want to run your own blockchain node including a web3 node.js implementation.

Token or Ether (ETH)

ICOs and tokens are the go-to buzzwords at the moment. But the aim is to keep a leaderboard as simple as possible by using standard ETH based transactions.

Costs

Deploying a smart contract on the mainnet does not come for free. As an example: the first version of the BlockScores contract was 0.1 ETH only for it’s deployment. 
But also transaction costs need to be as low as possible. Because of this, the contract storage and its functions have to be designed in a cost saving manner. An example: storing strings might be simple by design. But compared to byte32 (which need to be converted with web3.fromAscii() and .toAscii()) it is more expensive. The difference in gas usage is noticeable to the end-user when using your application.

Features vs Simplicity

How feature enriched should the DApp be? A one-time creation of a leaderboard? Or should the users receive a complex UI? Including personalization like custom CSS styles? Can players change their name or even add avatars? 
The goal for me was to reduce the UI to fit on a one-pager on mobile devices. A further option is to release a DApp on distributed storage like IPFS. In this case you also need to question the use of libraries like jQuery to reduce the footprint.


Smart Contract

In the following sections, I want to describe the basic structure of my smart contract. This can host many independent leaderboards. 
The code can be found here: https://github.com/Rello/BlockScores/blob/master/solidity/BlockScores_v0.1.sol

Header

The smart contract for BlockScores is built on two data structures. A mapping assigns various players to a game and identifies each player by it’s public address. 
It holds 2 score counters. An unconfirmed and the final (confirmed) score. Additionally, every player needs to have an isActive indicator. The reason is, that it is not possible to delete mappings in a contract storage. Due to this, application logic has to determine if a record is active or not.

struct Player {
bytes32 playerName;
address playerAddress;
uint score;
uint score_unconfirmed;
uint isActive;
}
struct Game {
bytes32 gameName;
string gameDescription;
uint numPlayers;
address gameOwner;
mapping (uint => Player) players;
}
mapping (bytes32 => Game) games;

Board functions

A board is identified by a hash calculated on the boards´s title and the address of the creator. The board hash is the URL-parameter on the frontend to access it’s data. This URL is only known to the creator. The board creator is also the only owner of the board for later maintenance purposes.

function addNewGame(bytes32 name, string gameDescription) public returns(bytes32 gameHash){
gameHash = keccak256(name, msg.sender);
numGames++;
games[gameHash] = Game(name, gameDescription, 0, msg.sender);
emit newGameCreated(gameHash, name);
}

Based on the board hash, the metadata can be retrieved via a constant function call. Constant in solidity means, that it is non-transactional and does not cost any gas.

function getGameByHash(bytes32 gameHash) constant public returns(bytes32,string,uint){
return (games[gameHash].gameName, games[gameHash].gameDescription, games[gameHash].numPlayers);
}

There are more functions to provide a least level of flexibility. They are available game maintenance as an example.

Player functions

Players add themselves and the contract links these to their addresses. In this case, the board object is accessed via a storage assignment. Additionally the function raises the player count each time. This is mandatory because a storage mapping holds all possible combinations during creation. Because of this, the function needs to know which ones are already used.

function addPlayerToGame(bytes32 gameHash, bytes32 playerName) public returns (bool) {
Game storage g = games[gameHash];
uint newPlayerID = g.numPlayers++;
g.players[newPlayerID] = Player(playerName, msg.sender,0,0,1);
return true;
}

Within the later UI, the number of players need to be looped incremental. Each isActive player is then returned one at a time. As solidity is not able to return arrays, web3 has to call this function numPlayers-times.

function getPlayerByGame(bytes32 gameHash, uint8 playerID) constant public returns (bytes32, uint, uint){
Player storage p = games[gameHash].players[playerID];
require(p.isActive == 1);
return (p.playerName, p.score, p.score_unconfirmed);
}

Score functions

Any user who knows the hash of the leaderboard can add a score. This function has to receive a valid player name. The players´ addresses are not workable for security reasons. 
When the player is active and part of the board, the function stores an unconfirmed score.

function addGameScore(bytes32 gameHash, bytes32 playerName, uint score) public returns (bool){
uint8 playerID = getPlayerId (gameHash, playerName, 0);
if (playerID < 255 ) {
games[gameHash].players[playerID].score_unconfirmed = score;
return true;
} else {
return false;
}
}

This function adds the unconfirmed scores to the finale score storage. To add a 4-eye layer, a player cannot confirm his own scores. The confirmation can only be performed by another isActive player of the same board.

function confirmGameScore(bytes32 gameHash, bytes32 playerName) public returns (bool){
uint8 playerID = getPlayerId (gameHash, playerName, 0);
uint8 confirmerID = getPlayerId (gameHash, "", msg.sender);
require(playerID < 255); // player needs to be active
require(confirmerID < 255); // confirmer needs to be active
require(games[gameHash].players[playerID].playerAddress != msg.sender); //confirm only other players
games[gameHash].players[playerID].score += games[gameHash].players[playerID].score_unconfirmed;
games[gameHash].players[playerID].score_unconfirmed = 0;
return true;
}

The complete solidity contract is available on GitHub.


Proof of Concept

The basic UI/UX implementation uses pure HTML/JS to create a responsive layout. It uses a simple table showing the players, totals and pending scores. For every player’s row, the interactions are available via buttons.

If you would like to test the working PoC, please have a look here.

I will share my lessons learned on the web3 implementation in a later article.

Outlook

BlockScores hosts smart contracts and the UI for various devices and platforms. These contracts enable the creation and management of leaderboards for any user.
Currently, there is the proof of concept already available on Rospen. The final version will be released soon, when the UI design is completed. 
The next development sprints plan deliver public APIs or BlockScores integration. For example integrated apps in private servers like Nextcloud or ownCloud can offer integrated leaderboard management.


Do you like this idea? If yes, clap some hands below or get connected on Twitter
This is my first article based on my first DApp implementation. Any feedback will help me to improve. Thank you.