Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Invalid

Irreplaceable Commit Blocks Progress and Traps Users

Summary

In the RockPaperScissors contract, once a player submits a commitment via commitMove, they cannot change it — even if they made a mistake (e.g., wrong hash, typo in salt). There is no mechanism to overwrite or reset a faulty commit. This leads to a situation where the player is locked out of the revealMove phase, causing the game to stall until the revealDeadline expires and someone calls timeoutReveal. This creates poor user experience, delays, and unnecessary lock-up of funds or tokens.

Vulnerability Details

In commitMove, the contract strictly enforces:

require(game.commitA == bytes32(0), "Already committed");

Once a commit is made, it is immutable. If the player realizes they made a mistake — for example:

  • Used the wrong move encoding (e.g., 0 instead of 1–3)

  • Mismatched salt

  • Pasted an incorrect hash

Then revealMove will fail:

require(commit == game.commitA, "Hash doesn't match commitment");

This leaves the player stuck. The only way for the game to proceed is to wait out the full timeoutInterval, then either:

  • The other player calls timeoutReveal, or

  • If neither revealed, the game is cancelled.

There is no option for the player to correct their own mistake and continue.

Impact

  • Poor UX: Players are punished for a simple mistake with long timeouts.

  • Unnecessary delays: Honest games are artificially slowed down.

  • Increased cost: Players waste time and ETH on gas with no gameplay benefit.

  • Trapped state: Games stall mid-turn and require manual timeout intervention.

Tools Used

  • Manual review of commitMove and revealMove

  • Analysis of game state transitions

  • Reasoning from user behavior and error cases

Recommendations

Add a mechanism to replace a commit before both players have revealed. For example:

  1. Allow a player to override their commit before revealDeadline is set:

    if (msg.sender == game.playerA && game.moveA == Move.None) {
    game.commitA = _commitHash;
    }
  2. Add a replaceCommit function with constraints:

    • Only allowed if opponent has not yet revealed

    • Only once per turn per player (prevent abuse)

    • Resets the reveal deadline if previously set

  3. Improve UX messaging in the frontend to remind users to double-check hash/salt before submitting.

  4. Optional: Allow a game-wide "abort turn" agreement if both players consent, and re-initiate the turn.

Updates

Appeal created

m3dython Lead Judge 2 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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