Security

The Illusion of Randomness: Why Your RNG Will Be Broken in 1 Block

Blockchain is a deterministic system. Every node in the network must reproduce the transaction history to arrive at the exact same state. True randomness (entropy) contradicts the very essence of determinism.

GameFi and NFT developers often ignore this foundation. They attempt to derive random numbers using the blockchain's internal tools, creating vulnerabilities that miners and validators exploit.

1. Native Randomness: The Rookie Trap The most common and dangerous pattern involves using global block variables as a source of entropy.

Typical vulnerable code: uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100;

Let's analyze why this fails.

  • block.timestamp: The validator creating the block controls its timestamp. Ethereum allows a time drift (usually up to 15 seconds relative to real time). If a 10 ETH jackpot is at stake, a validator will iterate through timestamp values until the formula yields a winning number. They are guaranteed to win because they "print" the winning ticket themselves.
  • blockhash: The hash of the current block is unknown at the moment of transaction execution (it is calculated afterwards). Using the previous block's hash (blockhash(block.number - 1)) is also unsafe: an attacker can perform a reorg attack or simply withhold the block if the result is unfavorable (in mining contexts).

2. The Economics of the Attack Many believe that a miner is too "lazy" or that it is "unprofitable" to manipulate a block for a small win. This is a mistake.

In the era of MEV (Maximal Extractable Value), this process is automated. Searchers and block builders scan the mempool for transactions relying on vulnerable RNG. If the potential profit from manipulation exceeds the block reward (or gas cost), the attack occurs instantly and automatically. Your contract's security must not depend on the "honesty" of miners.

3. The Merge & PREVRANDAO With Ethereum's transition to Proof-of-Stake, the block.difficulty field was replaced by block.prevrandao. Some developers mistakenly view this as a source of "true" randomness.

While prevrandao is harder to manipulate than difficulty, it is still biasable by the validator. A validator can choose to publish a block or skip a slot, thereby influencing the final random value on-chain. For High Value applications, this risk is unacceptable.

4. The Solution: Oracles (Chainlink VRF) The only way to obtain a provably fair random number is to move generation off-chain with subsequent on-chain verification.

The industry standard is Chainlink VRF (Verifiable Random Function).

  1. Request: The smart contract sends a request to the oracle (seed + fee).
  2. Generation: The oracle generates a number and a cryptographic proof that the number was derived correctly from the seed.
  3. Verification: The smart contract verifies the proof. If valid, the number is accepted.

No one (including the oracle and miners) can rig the result, as the proof is mathematically linked to the oracle's public key.

5. Commit-Reveal (The Alternative) If the budget does not allow for oracles (VRF costs gas), use the Commit-Reveal scheme.

  1. Participants submit a hash of their number (salt + number).
  2. After the betting period closes, participants reveal their original numbers.
  3. The final random value is calculated from the combination of all revealed numbers. The downside: A participant may refuse to reveal their number if they see they are losing, effectively blocking the game (griefing).

Conclusion There is no randomness inside the EVM. Any attempt to generate random numbers using the contract's own resources (timestamp, blockhash, msg.sender) creates a security hole. Use Chainlink VRF for critical mechanics. Saving on oracle fees today guarantees the loss of the entire bank tomorrow.