Bad Randomness in Solidity

Natachi Nnamaka
Rektify AI
Published in
6 min readNov 18, 2023

Introduction

It is easy to generate random numbers in programming languages like JavaScript or Python, but it is more challenging to do so securely in Solidity. Solidity needs secure randomness for different applications, such as games and lotteries. The reason is that blockchains are deterministic, meaning that the output of any function is always the same for the same input. This makes it difficult to generate truly random numbers on the blockchain, as any algorithm used to generate randomness could be exploited.

This article will cover the role of randomness in solidity, sources of bad randomness, how to get randomness using Chainlink oracle VRF functions, hacks, and mitigation techniques.

The Role of Randomness in Solidity

Randomness is an important element of many Web3 applications, including on-chain games, lotteries, and decentralized finance (DeFi) protocols. It is used to create unpredictable outcomes, which can be added to gameplay mechanics, ensure fair distribution of rewards, and prevent fraud.

For example, in a Web3 game, randomness can be used to determine the outcome of a battle, the rarity of an NFT item, or the distribution of loot. In a lottery, randomness is used to select the winning numbers. In DeFi protocols, randomness can be used to generate private keys, which are used to secure users’ funds.

When it comes to randomness, there are two different forms, namely pseudorandomness and true randomness. Pseudorandomness is generated by deterministic algorithms, while true randomness relies on unpredictable and unbiased sources of entropy.

Pseudorandom numbers appear to be random, but they are generated by a formula. This means that they can be predicted if you know the formula and the initial seed value.

True randomness is generated by physical processes that are inherently unpredictable. True randomness is used for more security-sensitive applications, such as generating cryptographic keys.

Bad Randomness

Bad randomness vulnerability occurs when a smart contract relies on a source of randomness that is not truly random or that can be predicted by an attacker. This can allow an attacker to manipulate the outcome of a transaction or gain an unfair advantage over other users.

There are different sources of bad randomness in smart contracts, and we will be looking at some of them:

  • Using block hash as a source of randomness:

A block hash is a value that is derived from the contents of a block on the Ethereum blockchain. It is considered to be pseudorandom because it is based on the transactions and other data contained in the block, but it is not truly random because it is determined by the contents of the block, which may be known or predictable to an attacker.

  • Using the current block timestamp as a source of randomness:

It is not recommended to use block.timestamp as a source of randomness because it can be influenced or manipulated by validators (miners). Validators have a degree of control over the block.timestamp value. They can adjust the timestamp of a block they are mining, as long as it is within a certain tolerance range. This means that miners can potentially manipulate the timestamp to generate specific outcomes in smart contracts that rely on block.timestamp for randomness. On a higher level, a front-running attack can occur.

  • Using local variables and storage as a source of randomness:

Local variables and storage variables in Solidity can be used to generate random values or to make decisions that should be unpredictable. However, the values of these variables are visible to all participants in the contract, making it possible for malicious actors to predict or manipulate the outcomes.

Hack Caused By Bad Randomness

We will be looking at a real-world hack that occurred due to bad randomness in a contract:

Fomo3D Hack

Fomo3D is a lottery game where the last person to buy a key wins. Players can buy keys during a round, and the price of keys increases slightly with each purchase. Players also earn passive income from the game as keys are bought. When the round ends, the last person to buy a key wins the pot.

What attracted players to the game were the airdrops. Airdrops are prizes that are given to random players. This also attracted attackers.

Winners are chosen completely randomly. The Fomo3D contract uses multiple sources of randomness to generate random numbers, such as block creation time, the miner address of the current block, and the address of the current player, which we explained above that they are sources of bad randomness.

Here is the formula used:

contract FoMo3Dlong is modularLong {
/**
* @dev generates a random number between 0-99 and checks to see if thats
* resulted in an airdrop win
* @return do we have a winner?
*/
function airdrop()
private
view
returns(bool)
{
uint256 seed = uint256(keccak256(abi.encodePacked(
(block.timestamp).add
(block.difficulty).add
((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (now)).add
(block.gaslimit).add
((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)).add
(block.number)
)));
if((seed - ((seed / 1000) * 1000)) < airDropTracker_)
return(true);
else
return(false);
}
}

Using this method to determine random winners for the airdrop makes the Fomo3D contract predictable and vulnerable to attack.

The attacker performed his attack by using a contract to deceive the Fomo3D contract that it was an EOA (because only EOA are eligible for airdrops and not contracts) and was also able to manipulate the randomness of the Fomo3D smart contract and get the airdrop, getting away with lots of ether.

Getting Randomness Using Chainlink VRF

Chainlink is a decentralized Oracle network that provides secure and reliable data feeds and off-chain computation for smart contracts. It allows smart contracts to access real-world data and events and perform complex off-chain computations without compromising their security.

Chainlink is used by a wide range of decentralized applications, including DeFi protocols, insurance platforms, and gaming applications. One of the major services of Chainlink is VRF (verifiable random function), in which you get random numbers.

Chainlink VRF is a provably fair and verifiable random number generator (RNG) that allows smart contracts to use random numbers without sacrificing security or usability. For each request, Chainlink VRF produces one or more random numbers and a cryptographic proof of how they were generated. The proof is published and verified on the blockchain before any applications can use it. This process ensures that no one, including Oracle operators, miners, users, or smart contract developers, can tamper with or manipulate the results.

Subscription and Direct funding methods are the two methods you can use to generate random numbers using Chainlink VRF.

The subscription method is good for regular requests for randomness where you need to request randomness frequently. With this method, you create a subscription account and pre-fund it with LINK tokens. Then, when you need randomness, you simply request it from your subscription account. VRF costs are calculated after your requests are fulfilled and then deducted from your subscription balance.

The direct funding method is good for infrequent, one-off requests for randomness. With this method, you don’t need to create a subscription account. Instead, you directly fund the consuming contract with LINK tokens before it requests randomness. VRF costs are estimated and charged at request time.

The main difference between the two methods is that the subscription method is more efficient for regular requests, while the direct funding method is more efficient for infrequent requests.

Chainlink provided a simple guide on how to use ChainlinkV2 VRF to generate random numbers in your smart contracts in their documentation

Mitigation Techniques:

To avoid using bad randomness in Solidity, it is important to implement secure randomness in your smart contracts when building your blockchain applications.

  • Use Chainlink VRF: Chainlink VRF is a secure way to generate random numbers in your smart contract projects. Whenever you want to implement randomness in your solidity contract, do not use block-related data, such as block hashes, block timestamps, or block numbers, to generate random numbers because they can be manipulated. It is advised to integrate Chainlink VRF.
  • Keep your smart contract up-to-date with the latest security fixes and best practices, and have it audited regularly to find any weaknesses, especially those related to randomness.

Conclusion

Bad randomness is a serious security flaw in Solidity contracts. It can be exploited by attackers to manipulate the outcome of smart contracts, leading to financial losses for users. Developers should be aware of this vulnerability and take steps to mitigate it, such as using decentralized oracles that provide secure randomness functions, such as Chainlink VRF.

--

--

Natachi Nnamaka
Rektify AI

I am a junior blockchain developer with a background in frontend development.