To ensure fairness, both players have to commit their moves before revealing. However, RockPaperScissors::revealMove
does not check if the opponent has committed. Hence, a player can accidentally reveal their moves in advance. Thus, the opponent can deduce and commit a winning move. Upon reveal, the opponent wins.
function revealMove(uint256 _gameId, uint8 _move, bytes32 _salt) external {
Game storage game = games[_gameId];
+ require((msg.sender == game.playerA && game.commitB != bytes32(0)) || (msg.sender == game.playerB && game.commitA != bytes32(0)), "Opponent has not commit a move");
require(msg.sender == game.playerA || msg.sender == game.playerB, "Not a player in this game");
require(game.state == GameState.Committed, "Game not in reveal phase");
require(block.timestamp <= game.revealDeadline, "Reveal phase timed out");
require(_move >= 1 && _move <= 3, "Invalid move");
Move move = Move(_move);
bytes32 commit = keccak256(abi.encodePacked(move, _salt));
if (msg.sender == game.playerA) {
require(commit == game.commitA, "Hash doesn't match commitment");
require(game.moveA == Move.None, "Move already revealed");
game.moveA = move;
} else {
require(commit == game.commitB, "Hash doesn't match commitment");
require(game.moveB == Move.None, "Move already revealed");
game.moveB = move;
}
emit MoveRevealed(_gameId, msg.sender, move, game.currentTurn);
// If both players have revealed, determine the winner for this turn
if (game.moveA != Move.None && game.moveB != Move.None) {
_determineWinner(_gameId);
}
}