Smart Contracts are not contracts and not smart. – Wolfgang Ahrendt

Introduction

On this page, we introduce the example case of casino implemented as a Smart Contract on the blockchain. This example was prepared by Wolfgang Ahrendt for discussion in September 2021, and also bases on Gordon Pace.

The casino offers a simple bet game. The casino operator places a (hidden) secret (HEAD or TAIL) inside the contract. Then, a player can challenge the casino by placing a bet, and guessing the secret. If players win, their bet is doubled, otherwise the deposit is taken. A play of the game is given in the next section.

From a service-oriented point of view, the casino is a component which offers several services (or methods) for altering the state, and only certain roles are allowed to trigger them.

A Play in the Casino

Let us consider a play of our casino game. We assume, that the casino exists. But it has no money in its account.

Firstly, the operator places money in the pot from his own money. They call the designated service addToPot.

Secondly, a new round needs to be initialised (createGame). The operator chooses a number, where the number’s least-significant bit determines whether the operator selected HEAD or TAIL. The hash of the number is stored inside the contract. This is a simple bit commitment protocol: The operator is not able to change their choice, and the player can not guess the operator’s choice as it is obfuscated by the other bits in the number.

The game is ready for a player, which is first one, who places a bet and guess (placeBet). The bet is transformed from the player’s wallet. The guess is boolean value: HEAD or TAIL.

The game of the looks as follows, and is ready to be decided.

If the players win, they become their bet doubled. If the oprator wins, the player’s money stays in the pot. In both cases, the casino is ready for a new play.

State Space

There is one operator over the casino’s lifecycle , and one at most one player at the time in the casino. State space of the game consists of:

Solidity Contract

First:

This contracts contains security issues. Please do not use this Smart Contract as the basis for your new disruptive business model.

/* This smart contract uses simplified Solidity syntax
   and does not compile as is. The original contract was
   written by Gordon Pace, and contained also a timeout
   as a counter measure against the operator not calling
   decideBet. The current simplified version was written
   by Wolfgang Ahrendt. It deliberately introduces
   security bugs. */

contract Casino {

  address public operator;
  
  uint public pot;
  
  // The hashed number submitted by the operator
  bytes32 public hashedNumber;
  
  address public player;
  
  enum Coin { HEADS, TAILS }
  Coin guess;

  uint bet;

  // The state of the contract
  enum State { IDLE, GAME_AVAILABLE, BET_PLACED }
  State private state;
  
  // Create a new casino
  constructor() public {
    operator = msg.sender;
    state = IDLE;
    pot = 0;
    bet = 0;
  }
  
  // Add money to pot
  function addToPot() public payable {
    require (msg.sender == operator);
    pot = pot + msg.value;
  }
  
  // Remove money from pot
  function removeFromPot(uint amount) public {
    // no active bet ongoing:
    require (state != BET_PLACED);
    require (msg.sender == operator);
    operator.transfer(amount);
    pot = pot - amount;
  }
  
  // Operator opens a bet
  function createGame(bytes32 _hashedNumber) public {
    require (state == IDLE);
    require (msg.sender == operator);
    hashedNumber = _hashedNumber;
    state = GAME_AVAILABLE;
  }

  //caller syntax: contractaddress.placeBet(HEADS).value(1000 Wei)
  
  // Player places a bet
  function placeBet(Coin _guess) public payable {
    require (state == GAME_AVAILABLE);
    require (msg.sender != operator);
    require (msg.value <= pot);
    
    state = BET_PLACED;
    player = msg.sender;

    bet = msg.value;
    guess = _guess;
  }
  
  // Operator resolves a bet
  function decideBet(uint secretNumber) public {
    require (state == BET_PLACED);
    require (msg.sender == operator);
    require (hashedNumber == cryptohash(secretNumber));
    
    Coin secret = (secretNumber % 2 == 0)? HEADS : TAILS;
    
    if (secret == guess) {
      // player wins, gets back twice her bet
      pot = pot - bet;
      player.transfer(2*bet);
      bet = 0;
    } else {
      // operator wins, bet transfered to pot
      pot = pot + bet;
      bet = 0;
    }
    
    state = IDLE;
  }
}

Explanation of the contract

Initially both, pot and bet, are empty. The hashed secret and the guess have a default value value. The program allows the following operations:

Note, Solidity has an internal transaction system. An external service calls starts a new transaction. If the execution fails, e.g., a require is not met, the transaction is rolled backed, and the state remains the same in the block chain.

Submitted Material