The function RapBattle::_battle uses block.timestamp and block.prevrandao to generate random number. This random number is then used to determine the winner of the battle. But relying on the block.timestamp and block.prevrandao for randomness can be risky and the generated random number can be predicted rather than random.
The RapBattle::_battle function determines the winner of the battle based on random number and rapper skills of the participants.
But the generation of random is based on block.timestamp, block.prevrandao, msg.sender which is not recommended.
Relying on block.timestamp for randomness is risky because the validator selected for a transaction has the power to hold or delay the transaction until a more favorable time or reject the transaction because the timestamp isn't favorable.
Timestamp manipulation has become less of an issue on Ethereum, since the merge, but it isn't perfect. Other chains, such as Arbitrum can be vulnerable to several seconds of slippage putting randomness based on block.timestamp at risk.
block.prevrandao replaced the block.difficulty. This is a system to choose random validators. The security issues using this value for randomness are described in the EIP-4399 documentation: https://eips.ethereum.org/EIPS/eip-4399#predictability
Additionally, in the Solidity documentation is written:
https://docs.soliditylang.org/en/latest/units-and-global-variables.html
The function RapBattle::_battle uses block.timestamp, block.prevrandao and msg.sender to generate a pseudo-random number for determining the winner of the battle. This is considered weak because miners have some control over the block.timestamp and block.prevrandao, which could potentially be exploited to manipulate the outcome of the battle. The block.timestamp influences the keccak256 function, and subsequently the _battle function, by being part of the seed that generates the pseudo-random number. In the _battle function, the seed for the keccak256 function is created by packing block.timestamp, block.prevrandao and msg.sender together using abi.encodePacked.
To demonstrate the weak randomness in the _battle function, we can write a test case that manipulates the block.timestamp and block.prevrandao using the vm.warp and vm.prevrandao functions in Foundry. The vm.warp and vm.prevrandao functions in Foundry give control over the simulated blockchain environment in the tests. These functions don't directly influence the calculation of the winner in the _battle function, but they do allow to manipulate the values that are used in that calculation. Add the following test case in file OneShot.t.test.sol and execute it with the Foundry command: forge test --match-test "testWeakRandomness" -vvvvv.
The test function testWeakRandomness demonstrates that in two battles with the same participants the generated random number and respectively the winner are the same.
Manual Review, Foundry
You might consider using a dedicated randomness oracle such as Chainlink VRF. https://blog.chain.link/random-number-generation-solidity/
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.