Generating random numbers in Solidity is not simple. For starters, Solidity doesn’t come with a native random function because the deterministic nature of the system.
In mathematics and physics, a deterministic system is a system in which no randomness is involved in the development of future states of the system. A deterministic model will thus always produce the same output from a given starting condition or initial state. — https://en.wikipedia.org/wiki/Deterministic_system
There are a number of services and APIs that allow us to generate random numbers, but the problem is that they are not decentralized and you have to trust someone else that the generated number is truly random.
Additionally, you can’t trust seemingly random values available to the contract, such as blockhash, timestamp, or other miner-defined value. Miners can manipulate these values to such extent, and event choose not to publish a block. For a casino app you can’t just trust miners, but there are a lot of other use cases where you could rely on blockhash for generating a random outcome, as long as the total amount of value resting on the quality of that randomness is lower than what a miner earns by mining a single block. More info on this: https://ethereum.stackexchange.com/questions/419/when-can-blockhash-be-safely-used-for-a-random-number-when-would-it-be-unsafe
So, how can we generate a random number securely inside a Solidity smart contract?
There are a few solutions out there that can help us achieve this, such as Randao (Randao: https://github.com/randao/randao.) and Oraclize (http://www.oraclize.it/#services). In this article I’m going to focus on Oraclize, mainly because it’s extremely easy to get started with it and secondly, because it also can be used to do many more things other than generating a random number, such as accessing any other API from inside a Solidity smart contract.
In this tutorial we’ll build a very simple raffle smart contract to show how to use Oraclize to generate a random number. The contract will work as follows:We’ll deploy the contract and set a minimum and maximum number of participants the raffle will accept. Once there are enough participants, the raffle organizer (or anyone for that matter) can execute the function that will have Oraclize generate a random number. With that random number we’ll just select the winner out of the participants’ array.*For this first version we won’t include all the payments logic, so anyone is free to participate but they won’t receive any ETH prize, of course.
For this tutorial, we’ll just deploy everything on Remix, using Metamask to connect to Ropsten, as Oraclize won’t work locally or on the JavaScript VM. I’m going to assume you have some basic understanding on how to work with Remix and Metamask. Also, make sure you have at least 3 or 4 accounts with some ether balance as we’ll need it to pay for the gas cost of joining the raffle as well as paying Oraclize to generate the random number (more on this later).
In Remix, create a new file called Raffle.sol, this will be our contract containing all the logic for the raffle.
pragma solidity ^0.4.4;
contract Raffle {
uint private chosenNumber;address private winnerParticipant;uint8 maxParticipants;uint8 minParticipants;uint8 joinedParticipants;address organizer;
bool raffleFinished = false;
address[] participants;mapping (address => bool) participantsMapping;
event ChooseWinner(uint _chosenNumber,address winner);event RandomNumberGenerated(uint);
function Raffle(){address _org = msg.sender;uint8 _min = 2;uint8 _max = 10;
require(\_min < \_max && \_min >=2 && \_max <=50);
organizer = \_org;
chosenNumber = 999;
maxParticipants = \_max;
minParticipants = \_min;
}
function() payable {}
function joinraffle(){require(!raffleFinished);require(msg.sender != organizer);require(joinedParticipants + 1 < maxParticipants);require(!participantsMapping[msg.sender]);
participants.push(msg.sender);
participantsMapping\[msg.sender\] = true;
joinedParticipants ++;
}
function chooseWinner(uint _chosenNum) internal{chosenNumber = _chosenNum;winnerParticipant = participants[chosenNumber];ChooseWinner(chosenNumber,participants[chosenNumber]);}
function generateRandomNum(){require(!raffleFinished);require(joinedParticipants >=minParticipants && joinedParticipants<=maxParticipants);
raffleFinished=true;
chooseWinner(0); //We'll replace this with a call to Oraclize service later on.
}
function getChosenNumber() constant returns (uint) {return chosenNumber;}
function getWinnerAddress() constant returns (address) {return winnerParticipant;}
function getParticipants() constant returns (address[]) {return participants;}
}
I’ll go over the most important parts of the code:
Go ahead and try the contract in Remix. Just copy and paste the code above and deploy the contract. You should be able to join the raffle with different accounts and then, after there are enough participant, you can call generateRandomNumber() to select a winner (It will always be the address at position 0 in the participants array).
Now that we have made sure the logic of the raffle is ok, it’s time to use Oraclize to generate a random number for us.
Integrating Oraclize in our smart contract is pretty easy and straight-forward. The fist step is to import the Oraclize API in our contract. Add the following line before the Raffle contract declaration.
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract Raffle is usingOraclize{...}
Notice that we also changed Raffle to inherit from usingOraclize.
Now that we imported Oraclize, we have access to its functions. One thing you should be aware of is that importing Oraclize this way will fail, for example, if you are using Truffle to compile your contracts. If you are using Truffle, you will have to make a local copy of the Oraclize file and rename it to “usingOraclize.sol” as Truffle needs the file have the same name as the contract.
The first thing we will do is add the Oraclize callback function. The way Oraclize works is by first making a query — we could be querying an external API, Wolfram’s Mathematica, or, like in our case, the built-in random number generator — which will fire a transaction to an Oraclize contract, and after that gets processed, it fires the callback function we define inside our contract with the corresponding result.
This is how the callback function will look like:
// the callback function is called by Oraclize when the result is ready// the oraclize_randomDS_proofVerify modifier prevents an invalid proof to execute this function code:// the proof validity is fully verified on-chain
function __callback(bytes32 _queryId, string _result, bytes _proof){// If we already generated a random number, we can't generate a new one.require(!raffleFinished);// if we reach this point successfully, it means that the attached authenticity proof has passed!require (msg.sender == oraclize_cbAddress());
if (oraclize_randomDS_proofVerify__returnCode(_queryId, _result, _proof) != 0) {// the proof verification has failed, do we need to take any action here? (depends on the use case)} else {// the proof verification has passedraffleFinished=true;
// for simplicity of use, let's also convert the random bytes to uint if we need
uint maxRange = joinedParticipants; // this is the highest uint we want to get. It should never be greater than 2^(8\*N), where N is the number of random bytes we had asked the datasource to return
uint randomNumber = uint(sha3(\_result)) % maxRange; // this is an efficient way to get the uint out in the \[0, maxRange\] range
chooseWinner(randomNumber);
RandomNumberGenerated(randomNumber); // this is the resulting random number (uint)
}}
First, we will make sure that raffleFinished is false so we don’t allow this function to be called more than once.
Then we are checking that the data we got has not been tampered with while being delivered to the smart contract. You can read more about that here: https://blog.oraclize.it/the-random-datasource-chapter-2-779946e54f49
If the verification has passed, meaning the data has not been tampered with, then we proceed to use the random number. We will be calling the chooseWinner() function that we previously used, but instead of passing a hardcoded 0 as parameter, we’ll pass the random number we generated.As you can see, the random number that gets generated uses the joinedPaticipants state variable to determine its max range. We are also firing an event to log the random number we generated.
The last thing we have to do is modify our generateRandomNum() function so when someone executes it, it makes the query to Oraclize.
function generateRandomNum(){require(!raffleFinished);require(joinedParticipants >=minParticipants && joinedParticipants<=maxParticipants);
oraclize\_setProof(proofType\_Ledger); // sets the Ledger authenticity proof
uint N = 4; // number of random bytes we want the datasource to return
uint delay = 0; // number of seconds to wait before the execution takes place
uint callbackGas = 200000; // amount of gas we want Oraclize to set for the callback function
bytes32 queryId = oraclize\_newRandomDSQuery(delay, N, callbackGas); // this function internally generates the correct oraclize\_query and returns its queryId
}
By calling oraclize_newRandomDSQuery(delay, N, callbackGas) we’ll have Oraclize generate a random number for us, and when it’s ready, the callback function we defined earlier will be fired. oraclize_newRandomDSQuery receives 3 parameters.
And that’s it! You can give it a try in Remix and see how it works. A few considerations and words of advice:
The contract we built today allows us to run a simple Raffle that people can join and then a winner is selected by using Oraclize to generate a random number we can be certain hasn’t been tampered with. There’s a lot of room for improvements and new features, such as allowing people to enter the Raffle with ether and win a prize.
Let me know in the comments section below if you have any suggestions or if you encounter any problems with the code.