Generating a random number in Solidity is not as straightforward as in some other programming languages, because the Ethereum blockchain is deterministic, meaning that multiple execution clients or nodes must come to the same conclusion about the state of the blockchain.
For many use cases using the new prevrandao global variable provides “good enough” randomness for their applications. For anything which is at risk of manipulation or when large amounts of funds may be at stake it is better to use an off-chain oracle to provide random data.
PrevRandao
Here is an example code snippet for how to use prevrandao to generate a random number.
uint randomNumber = block.prevrandao;
In PoS, validators are chosen randomly to propose blocks. This randomness is achieved using the PrevRandao value. It is a hash value that is computed by every validator in the network at the end of each epoch.
The final random number is computed by XORing all the individual random numbers computed by the validators. The combination with XOR ensures that even just one single honestly computed random number of one of the validators in an epoch will lead to a new unmanipulated random number. Once an epoch is finished, the newly computed randomness will be used to determine the validators in the next epoch.
Note that PrevRandao was only introduced recently and may not be available unless you are using the latest version of solc. It also relies on a newly introduced opcode in the EVM so may not be available on alternate chains in which case you can use something like this (from Solidity Snippets):
function random() private view returns (uint) {
uint hashNumber = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, msg.sender)));
return hashNumber % 100;
}
Last Revealer Problem
Validators can choose not to sign the data in their slot. If they don’t sign the data in their slot, no one else has the private key, so there is no way to get this data, and in this case, the PrevRandao update for the slot is simply skipped. If this happens to be the last slot in an epoch, then the final random value will be the one from the second last slot. An attacker could even have control of multiple validators in the last slots of an epoch. If an attacker controls the last four validators of an epoch, in each of the 4 slots, they can choose to sign or not, giving them 16 possible total randomness values to choose from. The block reward is roughly 0.044 ETH, and not signing means losing this reward.
In reality this is not likely to be exploited in the wild but if there is large financial motivation it is possible and that’s where an oracle can provide an alternative.
Chainlink VRF
Chainlink VRF is a provably fair and verifiable random number generator that enables smart contracts to access random values without compromising security or usability. It generates one or more random values and cryptographic proof of how those values were determined, which is then published and verified on-chain before any consuming applications can use it. This process ensures that the results cannot be tampered with or manipulated by any single entity.
Chainlink VRF v2 offers two methods for requesting randomness: subscription and direct funding. The subscription method is suitable for regular requests and supports multiple VRF consuming contracts connected to one subscription account. VRF costs are calculated after requests are fulfilled and then deducted from the subscription balance. On the other hand, the direct funding method is suitable for infrequent one-off requests, where each VRF consuming contract directly pays for its requests. VRF costs are estimated and charged at request time, making it easier to transfer the cost of VRF to the end user. However, this method has a higher gas overhead than the subscription method.
Chainlink VRF can be used for various applications that rely on unpredictable outcomes, such as building blockchain games and NFTs, random assignment of duties and resources, and choosing a representative sample for consensus mechanisms. By using Chainlink VRF, smart contract developers can ensure that their applications have reliable and fair randomness.
There is more information at: https://docs.chain.link/vrf/v2/introduction/
Generating true randomness in a deterministic environment such as the Ethereum virtual machine poses unique challenges, and developers must take into account security considerations when designing their random number generators.
If you are doing something fun and non-critical then PrevRandao is an easy way to get a random number. If you are building the future of finance then an oracle, like Chainlink VRF, provides a reliable and secure solution for generating verifiably random values.