Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Pseudorandom numbers using block.timestamp and block.difficulty

Summary

The provided function uses block.timestamp and block.difficulty to generate pseudorandom numbers for selecting a winner and determining rarity in the PuppyRaffle game. This approach could worked in the Ethereum Proof-of-Work (PoW) consensus mechanism, but it has issues in the Ethereum Proof-of-Stake (PoS) consensus mechanism.

Vulnerability Details

function selectWinner() external {
require(block.timestamp >= raffleStartTime + raffleDuration, "PuppyRaffle: Raffle not over");
require(players.length >= 4, "PuppyRaffle: Need at least 4 players");
uint256 winnerIndex =
@> uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;
address winner = players[winnerIndex];
uint256 totalAmountCollected = players.length * entranceFee;
...
@> uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;
if (rarity <= COMMON_RARITY) {
tokenIdToRarity[tokenId] = COMMON_RARITY;
} else if (rarity <= COMMON_RARITY + RARE_RARITY) {
tokenIdToRarity[tokenId] = RARE_RARITY;
} else {
tokenIdToRarity[tokenId] = LEGENDARY_RARITY;
}
...
{

Impact

In Pos block.timestamp is 12 seconds after the previous block so it is predictable (as spec here https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#compute_timestamp_at_slot).

Also block.difficulty will not be used in the same way as in PoW. After the merge to PoS, the EVM 0x44 opcode, which currently returns DIFFICULTY, will be replaced by PREVRANDAO, which is the output of the randomness beacon provided by the beacon chain in the previous block.

As per solidiry docs (https://docs.soliditylang.org/en/v0.8.18/units-and-global-variables.html#block-and-transaction-properties):

  • block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao (EIP-4399).

The function will not work as expected after the transition to PoS. The calculation of winnerIndex and rarity will not be valid because block.difficulty will not return the same kind of value as it does in PoW. This could lead to unpredictable and potentially unfair results in the raffle game.

Tools Used

Manual review

Recommendations

Suggested to use a Chainlink Oracle like VRF v2 to get a random number.

Updates

Lead Judging Commences

Hamiltonite Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

weak-randomness

Root cause: bad RNG Impact: manipulate winner

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.