Random Number Generation for Solidity Smart Contracts #1

--

By now, most people are aware of the problem that one faces when trying to generate random numbers in a smart contract. There is unfortunately no one-size-fits-all solution for this, so let me go through the existing solutions.

Short Recap of Fails

Let’s briefly look at the common first ideas and why they are bad. I won’t go into much detail here, because others have done a great job of doing so.

1. Using block variables

  • block.number: The number of the block.
  • block.timestamp: The timestamp of the block.
  • block.difficulty: The difficulty of the block, i.e., how many trailing zeros are sufficient for a successful new hash.
  • block.gaslimit: The gas limit of the block, i.e., the maximum allowed amount of gas per transaction.
  • block.coinbase: The block miner’s address.

Those are obvious bad choices, because they can be predicted by anyone or at least the miner. Some more easy (block.number) than others (block.difficulty).

What if we add a private seed to the contract? The resulting random number can be computed with a passed variable and the privately stored seed as inputs. However, this approach does not consider the impossibility to store private data inside a public network. Despite Ethereum having a concept of private memory in smart contracts, this storage can still be read by anyone running an Ethereum node. Reading private or internal state can be achieved by web3.eth.getStorageAt. Therefore, it merely increases the effort for someone trying to predict the randomness.

2. Using the block hash

Technically also a block variable, but it deserves its own section. A block hash in Ethereum is computed as the Keccak256, an early implementation of SHA-3. It is a one way function, and by requiring a certain amount of trailing zeros as well as the miner’s address as salt, the resulting hash cannot be predicted by anyone. Well, that’s at least the idea.

First, you have to use it right. That means, use a future block hash! If you use an old one, people can see it obviously. If you use the current block’s hash, it will be empty, because it hasn’t been mined yet.

How to use a future block hash?

The check for randomNumber != 0 is essential, because Solidity can only look back at 256 blocks. So if a player waits for more than 256 blocks, he could enforce it to be 0. This has been used to hack SmartBillions for example.

So all good with using the future block hash?

It depends! Are you allowing gambles with winning amounts higher than the block reward? Then be aware of miner manipulation. If we assume a block reward of 3 ETH, any gamble for more than 6 ETH actually gives miners an incentive to cheat on the gamble. While a miner cannot freely choose the hash for a block, he may choose not to publish a newly found block hash → influence on the randomness.

3. Commitment scheme

First versions of the commitment scheme exist since 1981. Have a look a Michael Blum’s coin flipping over the telephone. It’s an interesting read. We can simply use hashes in Solidity. So what’s the idea?

We use the naive idea I described in the teaser:

Each node computes a random number locally. It further broadcasts this random number. Since each node will do the same, one can compute the final random number using a function that takes the previously locally generated numbers as inputs and produces a single output, e.g., v₁⊕ v₂ · · · ⊕ vₙ.

But instead of broadcasting the random number, a node will compute the hash of that number first. This hash will be the commitment. It then broadcasts the commitment hash. How does that help?

As the name suggests, a node is then committed to its original secret number, because it’s impossible to find a collision (another number that produces the same hash). Therefore, in the subsequent reveal phase a node cannot change its secret number anymore. Naturally, each node starts with the reveal phase only after having received all other node’s commitments. The procedure will look like this:

  1. All participants, P1Pn, each generate a secret value, Vi.
  2. Pi computes the commitment hash for their secret value: Ci = H(Vi).
  3. Each Pi sends Ci first (instead of Vi) .
  4. After all Ci are received, each Pisends Vi. All participants can verify the receiving secret values by checking if Ci == H(Vi).
  5. Once all Vihave been revealed and verified, the result of the random number generation will be R = V1 ⊕ V2 ⊕ … ⊕ Vn. (XOR)
  6. Should one participant fail to reveal his Vi, he automatically looses.

Sounds too good to be true? You’re right. This only works for two nodes, e.g., in a casino with a bank and single player. I have implemented a proof of concept prototype for this in Solidity and AWS Lambda: https://github.com/gorgos/Highstakes-SmartRoulette.

Let’s see why this only works for two nodes:

The issue we are facing is the situation where the last node Pihas to reveal its value, since it can calculate R with its last secret value before anybody else, the last-revealer-problem. Thus, it might not be able to influence R any more as it is committed to its value Vi. However, it may choose not to reveal the value, leaving all other parties no other option than to abort the random number generation. As in the two-user scenario, the not revealing node may loose the gamble. Nevertheless, that is not sufficient this time. Since there may be multiple users behind an entity and only the not-revealing-party looses, an attacker may do the following:

  1. Create as many entities as wanted and participate in the gamble with all of them.
  2. In the reveal phase, hold back the secret value of his last entity.
  3. Wait for every other entity to reveal their value and then compute the final result. If it yields a positive outcome in the sum for all participating entities, choose to reveal the value of the last entity. Otherwise, never reveal the last value. The gamble must be aborted and players will be refunded. The attacker only lost the gamble with a single entity.

Stay tuned, in the next article, we will look how to solve that problem.

--

--

Markus Waas — soliditydeveloper.com

Lead smart contract developer at InjectiveProtocol. Passionate about the revolution in finances. And sport. And healthy food.