An Ethereum Project (4) – The Lottery Orchestrator

In my previous post I described a instance of a single lottery draw. But how does a punter who wants to buy a ticket know where to buy it?

In the world of Ethereum that is not so easy. You can install geth on your computer and then create a wallet and put some ETH into it. After that, you would construct a draw object with the address and abi definition of the draw and then call draw.buyTicket with the right amount of gas to make sure your transaction goes through.
This is a process that excludes 99.99% of the population of the world!

There is a slighlty easier way, which is to use the Ethereum Mist wallet, which does most of the above for you. All you have to do is create your wallet and put ETH into it. You can then “follow” a contract (e.g. a draw) and call its methods (e.g. buyTicket).

But every instance of a draw will be at a different address on the blockchain, so how do you find the current one?

Enter Lottereo, our Lottery Orchestrator! Lottereo is basically a register of draws. It knows where the latest draw is and lets you buy tickets in the it. It will also give you information about the winners of previous draws. And all you have to do is follow the Lottereo contract in your Mist browser.

Let’s take it one step at a time. The contract defines a few variables to begin with, to hold draw information:

contract lottereo {
   address owner;
   uint public numDraws;
   struct drawData {
      uint drawDate;
      address eth_address;
   }
   mapping(uint => drawData) public draws;
    .
    .
    .
}

Its constructor defines the owner and initialises the number of draws to 0:

 function lottereo()  {
      numDraws = 0;
      owner = msg.sender;
   }

And we can define some simple functions to add a new draw, get the latest and the previous draws:

 function addDraw (address _eth_address) {
      if (msg.sender != owner) throw;
      draws[numDraws] = drawData(now, _eth_address);
      numDraws += 1;      
   }

   function getLatestDraw () constant returns (address _latest) {
      if (numDraws == 0) throw;
      _latest = draws[numDraws-1].eth_address;
   }

   function getPreviousDraw () constant returns (address _previous) {
      if (numDraws < 2) throw;
      _previous = draws[numDraws-2].eth_address;
   }

The Orchestration Bit

Crucially, within it is another contract call we call "drawInterface". This is basically a class with some "shell" methods in it which are a carbon copy of those in the draw contract:

contract drawInterface {

  uint public winningNumber;  

  function getPot() constant returns (uint) {
  }
 
  function buyTicket (address _buyer, uint _guess) {
  }
  
  function getPrizeValue (address _query) constant returns(uint) {
  }

}

This allows us to define variables of this class that live at a specified address (i.e. a draw instance on the blockchain) and then call the methods that exist inside that specific instance (because they have the same name in the draw contract and in the Lottereo class definition). So, for example, this is how you buy a ticket through Lottereo for the current draw:

 function buyTicket(uint _guess)  {
     drawInterface draw = drawInterface(getLatestDraw());
     draw.buyTicket.value(msg.value)(msg.sender,_guess);
   }

And this is how you know what the winning number is of the last draw that was drawn:

 function getWinningNumber ()  constant returns (uint _winner){
      drawInterface draw = drawInterface(getPreviousDraw());
      _winner = draw.winningNumber();
   }

So now, a user only has to be able to install the Mist Browser and know the address and ABI definition of the Lottereo contract to play in whichever draw is the current draw. So maybe now we are only excluding 99% of the population instead of 99.99% :-)

The Lottereo contract is here. Again, this is work in progress so things may change from what is described above.

Want to try it?

The Mist browser allows you to point to the real Ethereum blockchain or to the test blockchain, called Morden. If you point to Morden and create a wallet there, you can then get some fake ETH from here.

Our current Lottereo contract is at this address:

0x93ff7ee96a55f777f311f511b19586393f5598df

And its abi definition is:

[{
    constant: true,
    inputs: [{
        name: "",
        type: "uint256"
    }],
    name: "draws",
    outputs: [{
        name: "drawDate",
        type: "uint256"
    }, {
        name: "eth_address",
        type: "address"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getPot",
    outputs: [{
        name: "pot",
        type: "uint256"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getLatestDraw",
    outputs: [{
        name: "_latest",
        type: "address"
    }],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "_guess",
        type: "uint256"
    }],
    name: "buyTicket",
    outputs: [],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "_eth_address",
        type: "address"
    }],
    name: "addDraw",
    outputs: [],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getWinningNumber",
    outputs: [{
        name: "_winner",
        type: "uint256"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "numDraws",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getPreviousDraw",
    outputs: [{
        name: "_previous",
        type: "address"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "_query",
        type: "address"
    }],
    name: "getPrizeValue",
    outputs: [{
        name: "_value",
        type: "uint256"
    }],
    type: "function"
}, {
    inputs: [],
    type: "constructor"
}]

So have a go and tell us what you think!