An attacker can wait to join a battle until they are sure they will win the contest. This is due to the fact that no real randomicity is implemented, so any user can compute the result of a battle before joining it: if the pre-computed result shows they would win, they can enter the stage and be certain of obtaining the prize.
The RapBattle.sol::_battle
determines the final winner of a battle using the following computation:
and then proceeds to use the "random" value to determine the winner.
However, this value can be pre-computed by any attacker interested in joining the battle. The attack vector is as follows:
a user enters the stage with their rapper NFT, and waits for a challenger
the attacker, seeing that someone as entered the stage, starts computing what the value of "random" would be if they entered the battle
if the value of random would make the first user the winner, they wait for the variables to change, and recompute "random" (basically, wait for a new block)
when the new value of random would make the attacker the winner, they insert their transaction to challenge the first user, and win the battle and the bet associated with it
This approach is shown using the following test case, which can be added to the OneShotTest.t.sol
file:
If there is no random outcome, users will participate in a battle only when they are certain of winning. Initially, this will lead to an unfair advantage to users aware of this issue. Finally, as the fact that all battles are tricked becomes obvious, users will stop participating in battles, leading this functionality of the protocol to a halt.
Manual review, VSCode, Foundry
The use of block.timestamp
and block.prevrandao
does not guarantee real randomicity. Randomicity would greatly benefit from the usage of a VDF solution.
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.