The RockPaperScissors.sol
contract allows the game creator (Player A) to specify a _timeoutInterval
parameter during game creation (createGameWithEth
, createGameWithToken
). This interval determines the duration players have to reveal their moves after both have committed. While the contract enforces a minimum value for this interval (5 minutes), it crucially lacks validation for a maximum value. This allows Player A to set an arbitrarily long timeout period (e.g., weeks, months, or years). If Player B joins such a game without verifying the interval, and a situation arises where the timeoutReveal
function is needed (e.g., one player becomes unresponsive after committing), the game resolution and associated fund release will be delayed until the extremely distant revealDeadline
passes. This enables griefing by Player A and leads to impractically long fund locking periods for both players.
Affected Functions: createGameWithEth
, createGameWithToken
.
Input Parameter: _timeoutInterval
(uint256), representing the duration in seconds for the reveal phase.
Existing Validation: The functions correctly check for a minimum duration:
Missing Validation: There is no corresponding check to enforce a maximum reasonable value for _timeoutInterval
. Player A can supply any value greater than or equal to 5 minutes.
Deadline Calculation: The revealDeadline
is calculated within the commitMove
function after both players have committed:
If game.timeoutInterval
(set during creation by Player A) is excessively large, game.revealDeadline
will be set far into the future.
Timeout Function Dependency: The timeoutReveal
function, which handles scenarios where one or both players fail to reveal, requires the deadline to have passed:
Exploitation: If game.revealDeadline
is set extremely far in the future due to a large _timeoutInterval
, the timeoutReveal
function becomes unusable until that distant point in time. Any situation requiring a timeout resolution (e.g., Player B refuses to reveal a losing move) results in the game state remaining Committed
and funds remaining locked.
Game Creation: Malicious Player A calls createGameWithEth
with _timeoutInterval = 31536000
(approximately 1 year) and a standard BET_AMOUNT
.
Game Joining: Unsuspecting Player B finds the game and calls joinGameWithEth
with the matching BET_AMOUNT
, without checking the game's specific timeoutInterval
parameter (which is publicly readable from the games
mapping).
Commit Phase: Both Player A and Player B successfully call commitMove
. The revealDeadline
for the game is now set to block.timestamp + 1 year
.
Reveal Phase & Malice: Player A reveals their move. Player B observes Player A's move, realizes they will lose the turn (or the game), and decides not to call revealMove
.
Timeout Attempt: Player A waits a reasonable time (e.g., 1 hour) and attempts to resolve the game by calling timeoutReveal
.
Result: The timeoutReveal
call reverts with the message "Reveal phase not timed out yet"
because block.timestamp
is far less than the revealDeadline
(1 year in the future).
Outcome: The game remains stuck in the Committed
state. Both Player A's and Player B's BET_AMOUNT
remain locked in the contract. Player A cannot claim their win, and Player B cannot get a refund (nor can the game be cancelled via this path) until the 1-year deadline passes. Player A has successfully griefed Player B (and themselves) by locking funds for an unreasonable duration.
Griefing: Allows Player A to impose extremely long fund-locking conditions on Player B if a timeout scenario occurs. Player A can potentially force such a scenario by refusing to reveal themselves if they are losing later in a multi-turn game, although this also locks their own funds.
Prolonged Fund Locking: Staked ETH or Tokens for both players can be locked within the contract for impractically long periods (potentially years) if a timeout resolution is required.
Poor User Experience: Player B may unknowingly join a game with unreasonable timeout terms, leading to significant frustration and locked assets if their opponent becomes unresponsive or malicious.
Reduced Game Liveness: Games requiring timeout resolution become effectively stalled and unusable for the duration of the excessively long timeout interval.
Manual Code Review
Enforce a reasonable upper limit on the _timeoutInterval
parameter during game creation to prevent abuse.
Add Maximum Timeout Validation: Modify createGameWithEth
and createGameWithToken
to include a require
statement checking for a maximum allowed timeout. The specific maximum should be chosen based on reasonable gameplay expectations (e.g., 1 week, 30 days).
Client-Side Validation/Display: User interfaces interacting with the contract should clearly display the timeoutInterval
for a game before Player B joins, allowing them to make an informed decision about the game's terms.
Implementing Recommendation 1 (adding the require
check for a maximum timeout) is crucial to prevent the griefing and prolonged fund locking vulnerability.
Protocol does not provide a way for Player B to exit a game and reclaim their stake if Player A stops participating
Protocol does not provide a way for Player B to exit a game and reclaim their stake if Player A stops participating
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.