Solidity Pitfalls: Random Number Generation for Ethereum

Share this article

Solidity Pitfalls: Random Number Generation for Ethereum

This article was created in partnership with iOlite. Thank you for supporting the partners who make SitePoint possible.

Solidity is a fairly new language and as no code is perfect, it contains issues related to the code and what you want to accomplish with it. This article will guide you through the best practices and pitfalls when using a random number as input for your Ethereum smart contract.

Solidity Random Number Generation

Solidity is not capable of creating random numbers. Actually, every algorithm for creating random numbers is pseudorandom — no language is capable of creating completely random numbers. The problem with Solidity is that complex algorithms cost too much, so more basic solutions are used. Besides that, Solidity code should be deterministic, as it will run on multiple nodes. We need an algorithm that is able to generate a random number once, and use it on multiple nodes. Things like a clock time are not available for generating random numbers, so we have to look at other options. As a developer, you should be aware of this problem because an attacker is able to predict the result in some specific cases.

One of the most commonly used algorithms is ‘linear congruential generator’ (LCG). It’s one of the oldest algorithms, fast, and easy to understand. LCG is a good option for embedded systems as they have a limited amount of memory. However, it is not well-suited for cryptographically secure applications. Although, this is still being used in smart contracts as a fast algorithm is much cheaper to implement in terms of gas costs.

The algorithm itself performs these steps:

  • Accept input
  • Execute algorithm on the input
  • Take modulus of output (divide by max number in the range you require)
  • Output between 0 and max number in the range you require

Let’s explore the different ways of creating random numbers using a Lottery smart contract example. Users are able to join the Lottery by sending 0.1 Ether to the contract together with an integer between 0 and 250.

1. Block.timestamp & Block.difficulty

A block.timestamp is assigned by the miner whenever he confirms the transaction. No player of our Lottery contract is able to control it. Let’s take a look at this piece of code for creating a random number.

function random() private view returns (uint8) {
  return uint8(uint256(keccak256(block.timestamp, block.difficulty))%251);
}

Find the Gist here.

This piece of code first hashes a block timestamp and difficulty. Next, we convert the hash to an integer and divide it by 251 to get an integer between 0 and 250. However, the problem with this piece of code is that we shouldn’t trust a miner for picking a winner.

2. Lottery Input – Arbitrary Data

We need more arbitrary data for picking our winner. We can use the addresses of the players that have entered our lottery smart contract, but we have to hide it from other players as they can abuse it. Hiding this information is not possible as it’s all recorded on the blockchain.

The numbers submitted to our lottery smart contract can be used. Users have to hash the number they pick together with their Ethereum address. This gives us a pretty random number.

3. Chainlink VRF

This all being said, randomness is achievable, but you just have to use an oracle to get a random number from outside the blockchain. The issue with working with external data is that it’ll be really difficult to prove that the number is actually random, and make sure the entity off-chain isn’t manipulating the random number in any way. This is where Chainlink VRF comes into play. Chainlink VRF (Verifiably Random Function) is how we can get provably random numbers in solidity.

Chainlink VRF adds an event to the blockchain that the Chainlink node reads from, and returns with a random number. There is an on-chain randomness check that happens through what’s called the VRF Coordinator. This uses a specific key hash from the oracle and a seed phrase from the user mixed with some cryptography to make sure the number is truly random. This way, we can have an unbiased random number.

4. Other Mechanisms

4.1 Ethereum Alarm Clock

Developers need to think about when to pick a winner. Things like clock time are not available in the Ethereum Virtual Machine, as the code will run on multiple nodes, on a different time. This makes it even harder to pick a winner. One way to do it is by implementing a function in your smart contract that will close the lottery and pick a winner. This is not as decentralized as we want it to be. The owner of the contract can close the lottery when he is sure a friend of theirs will win. We want to avoid this kind of cheating.

A better option is to use the Ethereum Alarm Clock. It is a service that allows scheduling transactions to be executed at a later time on the Ethereum blockchain. This service is completely trustless, meaning that the entire service operates as a smart contract. Basically, the Ethereum Alarm Clock is using the block number to schedule transactions. Watch out, it doesn’t mean that the contract wakes up on its own. It relies on users having an interest (Ether reward) for calling the ‘pick winner’ function. Of course, if nobody calls your function, your lottery will fail.

4.2 Random Data Input

Random.org provides an API which gives you a random source of data through JSON. An Ethereum smart contract can use this source of data to feed an algorithm that picks a random number. As security is important, it is possible to use digital signing. The random data will be signed by Random.org. You can verify the integrity of the data so you can prove it really came from Random.org and the data hasn’t been tampered with.

RANDAO is a new project in the blockchain space that is solely focused on providing random numbers. They use a combination of oracles and smart contracts to provide you with random numbers. However, the RANDAO service is pretty slow at the moment. This is not ideal if you have an application that is often used.

4.3 Blocknumber Watcher

You can also use a watcher in your code, which checks the block number until it matches the target number you have set.

function f( blocknumber, to_address, value_) { 
  var filter = web3.eth.filter('latest').watch(
    function(err, blockHash) { 

      var target=blocknumber; 

      if(web3.eth.blockNumber==target) { 
        filter.stopWatching(); // your function here 
        web3.eth.sendTransaction({to:to_address, from:web3.eth.coinbase, value: web3.toWei(value_,"ether")});
        filter = null; 

        console.warn('Block reached'); 

        if (callback) return callback(false);
        else return false;

      } else { 
        console.log('Waiting the block'); 
      } 
  }); 
};

Source. Gist.

4.4 iOlite Smart Contract Creation

iOlite is creating a product that accepts natural language to create smart contracts. It is using a Stanford Natural Language Processing (NLP) engine called Fast Adaptation Engine (FAE). iOlite relies on community training by Solidity experts. Solidity experts (contributors) can define structures containing one or more sentences and attach it to the corresponding smart contract code.

The Stanford NLP engine has been created to understand complex language. The level of language complexity depends on the amount of machine training. After appropriate training, the engine will be capable of creating complex smart contracts. The FAE is capable of creating such contracts, as a complex contract is actually not that complex. Experts can split the request into multiple smaller pieces of code and attach it to one sentence.

When someone inputs multiple sentences, it will look for corresponding structures/sentences to build the ‘complex’ contract. Contributors will be rewarded with iOlite tokens via the mining process of new structures.

The benefit of using iOlite is that smart contract experts can solve difficult problems for you like the generation of random numbers. You can find more information at iOlite.io.

Conclusion

As you can see, it isn’t an easy task to generate true random input. Do not rely on block.timestamp, now and block.blockhash as a source of randomness. A good solution includes a combination of several pseudorandom data inputs and the use of oracles or smart contracts to make it more reliable. You need to be 100% sure nobody can tamper with the data that’s being inputted into your smart contract.

Be careful and think twice before implementing random number generation logic.

Frequently Asked Questions (FAQs) about Random Number Generation in Solidity

Why is it difficult to generate random numbers in Solidity?

Solidity, the programming language used for writing smart contracts on Ethereum, doesn’t have a built-in function to generate random numbers. This is because blockchain, the technology underlying Ethereum, is deterministic in nature. It means that given a set of inputs, the output will always be the same. This deterministic nature is crucial for maintaining the integrity and security of the blockchain. However, it makes generating truly random numbers a challenge because the concept of randomness is inherently non-deterministic.

What are the common methods used for generating random numbers in Solidity?

There are several methods used by developers to generate pseudo-random numbers in Solidity. One common method is using the keccak256 hash function with inputs that are difficult to predict, such as the current block timestamp and block difficulty. Another method is using an oracle service that provides random numbers from an off-chain source. However, each of these methods has its own limitations and potential security risks.

What are the risks associated with using the keccak256 hash function for generating random numbers?

While the keccak256 hash function can be used to generate pseudo-random numbers, it has some potential security risks. Since the inputs to the hash function, like the current block timestamp and block difficulty, are publicly available on the blockchain, a malicious miner could potentially manipulate these values to influence the outcome of the random number generation.

How can oracle services be used for generating random numbers in Solidity?

Oracle services can provide random numbers from an off-chain source. These services act as a bridge between the blockchain and the outside world, allowing smart contracts to interact with data that is not inherently available on the blockchain. However, using an oracle service introduces a level of trust, as the smart contract must rely on the oracle to provide accurate and unbiased random numbers.

What is the role of commit-reveal schemes in generating random numbers?

Commit-reveal schemes are a method used to generate random numbers in a decentralized and secure manner. In a commit-reveal scheme, participants first commit to a secret number, then all the secret numbers are revealed at once, and a random number is generated based on these secrets. This method prevents any single participant from being able to influence the outcome of the random number generation.

Why is it important to generate truly random numbers in smart contracts?

Truly random numbers are crucial in many types of smart contracts, such as those used for games of chance, lotteries, and other applications that require randomness. If the random numbers used in these contracts can be predicted or influenced, it could lead to unfair outcomes or even allow malicious actors to exploit the contract.

Can the blockhash function be used to generate random numbers in Solidity?

The blockhash function in Solidity can be used to generate pseudo-random numbers. This function returns the hash of a given block number, which is unpredictable and changes with every block. However, this method has its limitations. For instance, the blockhash function only works for the 256 most recent blocks, and the blockhash of a future block cannot be known until it is mined.

What are the limitations of using the current block timestamp for generating random numbers?

Using the current block timestamp as an input for generating random numbers has a major limitation. Miners have some influence over the timestamp of the blocks they mine, which means they could potentially manipulate the timestamp to influence the outcome of the random number generation.

How can the RANDAO beacon be used to generate random numbers?

The RANDAO (Random Number DAO) beacon is a decentralized and transparent method for generating random numbers. Participants in the RANDAO beacon commit to secret numbers, which are then revealed and combined to generate a random number. This method is designed to prevent any single participant from being able to influence the outcome of the random number generation.

Are there any upcoming improvements or proposals for generating random numbers in Solidity?

There are ongoing research and proposals for improving the generation of random numbers in Solidity and other blockchain platforms. For instance, Ethereum 2.0, the upcoming upgrade to the Ethereum network, is expected to include a built-in random number generator. However, until these improvements are implemented, developers must continue to use the existing methods with their inherent limitations and potential security risks.

Michiel MuldersMichiel Mulders
View Author

Fullstack Blockchain Developer at TheLedger.be with a passion for the crypto atmosphere.

ethereumiolitejoelflearn-ethereumrandom number generatorsoliditysponsored
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week