Source of Randomness
The Vulnerability
In the world of Ethereum smart contracts, generating a random number can be a tricky business. Two commonly used sources for randomness are blockhash and block.timestamp. However, these are not reliable sources for randomness and can lead to vulnerabilities in your contract.
Consider the following contract, GuessTheRandomNumber, a game where you win 1 Ether if you can guess the pseudo-random number generated from blockhash and block.timestamp.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract GuessTheRandomNumber {
constructor() payable {}
function guess(uint _guess) public {
uint answer = uint(
keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp))
);
if (_guess == answer) {
(bool sent, ) = msg.sender.call{value: 1 ether}("");
require(sent, "Failed to send Ether");
}
}
}At first glance, it seems impossible to guess the correct number. But let’s see how easy it is to win.
Alice deploys
GuessTheRandomNumberwith 1 Ether.Eve deploys
Attack.Eve calls
Attack.attack()and wins 1 Ether.
What happened? Attack computed the correct answer by simply copying the code that computes the random number.
Preventative Techniques
The key takeaway here is to avoid using blockhash and block.timestamp as sources of randomness. These values can be manipulated by miners, and as we’ve seen, they can be easily predicted by other contracts.
Instead, consider using an oracle to provide randomness. Oracles are third-party services that provide data to smart contracts. There are several oracles that provide random numbers, such as Chainlink VRF (Verifiable Random Function).
Last updated