How to “safely” generate random numbers in solidity contracts.

Tiago Bértolo
4 min readSep 6, 2019

--

Don’t generate random numbers inside your contracts. It won’t work, you will get hacked.

I ignored this advice when I wrote my first contract. I got punished!

Is it possible to generate random numbers inside a Solidity contract?

No! Everything in the blockchain is visible and publicly available. You need an outside random data source.

How do I get hacked if I generate a pseudo random inside a solidity contract?

I wrote a smart contract to simulate a wheel of fortune that used a “broken” pseudo random number generator. You can check the “fixed” version of the game at wheelspin.io. Give it a try!

wheelspin.io logo, give it a try if you feel lucky!
Wheelspin.io Logo. Give it a try if you feel lucky! 😃

The game is simple: the user selects a number X and spins the wheel. If the wheel spins result is between 1 and X the user wins.
You can also use the contract as a vault, no one can touch your funds while deposited there.

This is the broken code that “simulated” a random number:

function random() private returns (uint) {
uint randomnumber = uint(keccak256(abi.encodePacked(now, msg.sender, nonce))) % 100;
randomnumber = randomnumber + 1;
nonce++;
return randomnumber;
}

nonce, was a “private counter”, it can easily be accessed using web3.eth.getStorageAt(contractAddress, index).

now, is the same for the whole duration of the contract call.

The attacker created a malicious contract with this function in it:

His contract would generate randomnumber exactly with the same preconditions that were set in my contract. So all he had to do in order to win was call the bet function on my contract with the desired bet and the randomnumber + 2 in order to win every time.

My Ether… gone. Hopefully it wasn’t much. 😤

So… What is the best solution to safely generate random numbers inside a solidity contract?

You don’t. You get them from the outside world using an oracle (Oracles are services that send and verify real world occurrences and submit this information to smart contracts, triggering state changes on the blockchain). You will have to trust a data source, which is not a perfect solution.

The Oracle we are going to use is Provable.
We will use it to retrieve random data from a data source. We will also have a way to verify the data was not “manipulated” since it was generated.

We will use a method called provable_newRandomDSQuery.

This method accepts 3 parameters:

  • QUERY_EXECUTION_DELAY, will always be 0, we want to generate the random number A$AP.
  • NUM_RANDOM_BYTES_REQUESTED, will be a constant of 7.
  • GAS_FOR_CALLBACK, will be fixed at 200000 Wei.

The random number will be received asynchronously, but for now this function only returns a very important parameter called queryId.

This will later be used to match the ongoingBet that needs to be settled with the random number we just received.

The code of the bet function:

Ignore all those requires and balance manipulation, they are just game logic.

Provable service will call a function on my contract to deliver the random data. Like this:

  • _queryId, will be used to find the ongoingBet waiting to receive a random number.
  • _result, random bytes that will be used seed the generation of a random number.
  • _proof, a sequence of bytes that allow us to confirm that the random bytes generated by the data source were not tampered with.

So first we check that the contract calling our callback is the real Provable service but checking if it is equal to provable_cbAddress().

Then we verify that _queryId is not “null”, that our random data was not maliciously manipulated and is the same that our data source provided.

If everything is as expected we can now generate the random number using the random data and settle the ongoing bet:

uint randomNumber = uint(keccak256(abi.encodePacked(_result))) % ceiling;

The random bytes are encoded, hashed, converted to uint and then a ceiling is applied.

You can check the Wheelspin.io contract that uses ProvableAPI at https://github.com/wheelspinio/wheelspin-io-contracts. The contract is deployed in the Mainnet at https://etherscan.io/address/0xf17b52226d78070696ff2dddcb08bb65986054e1.

--

--