- Contract generates \"random\" values using on-chain data: `block.timestamp`, `blockhash`, `block.difficulty` / `block.prevrandao`, `block.number`, or combinations thereof
block.timestamp, blockhash, block.difficulty / block.prevrandao, block.number, or combinations thereoffunction drawLottery() external {
// All inputs are deterministic and publicly visible
// Another contract can compute the same value in the same tx
uint256 random = uint256(keccak256(abi.encodePacked(
block.timestamp,
block.prevrandao,
msg.sender
))) % 100;
if (random < 5) {
_payWinner(msg.sender);
}
}
// Attacker contract
contract Exploit {
function attack(Lottery target) external {
// Compute the same "random" value before calling
uint256 random = uint256(keccak256(abi.encodePacked(
block.timestamp,
block.prevrandao,
address(this)
))) % 100;
// Only call if we'll win
if (random < 5) {
target.drawLottery();
}
}
}
block.timestamp, block.prevrandao, block.difficulty, blockhash, block.number used as inputs to keccak256 or arithmetic operations producing a "random" valueblockhash is used for a future block (returns 0 for blocks not yet mined) or a block older than 256 blocks (also returns 0)block.prevrandao on PoS Ethereum provides sufficient entropy for the specific use case (not for high-value outcomes)block.timestamp, blockhash, or block.prevrandao alone for randomness in high-value contextsimport {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
contract FairLottery is VRFConsumerBaseV2 {
function requestRandom() external {
requestRandomWords(keyHash, subId, confirmations, gasLimit, 1);
}
function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
uint256 winner = randomWords[0] % participants.length;
_payWinner(participants[winner]);
}
}