Skip to content

Commit

Permalink
Completed the smart contract side (#5)
Browse files Browse the repository at this point in the history
* updated

* Completed the smart contract side
  • Loading branch information
jimmychu0807 authored Aug 15, 2024
1 parent 51fe92d commit 934c8c9
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 23 deletions.
104 changes: 85 additions & 19 deletions apps/contracts/contracts/GuessingGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.23;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGuessingGame} from "./interfaces/IGuessingGame.sol";
import {MIN_NUM, MAX_NUM} from "./base/Constants.sol";
import {MIN_NUM, MAX_NUM, ROUND_TO_WIN} from "./base/Constants.sol";

contract GuessingGame is IGuessingGame, Ownable {
Game[] public games;
Expand All @@ -30,18 +30,10 @@ contract GuessingGame is IGuessingGame, Ownable {
_;
}

modifier gameStateIn2(uint32 gameId, GameState[2] memory gss) {
Game storage game = games[gameId];
if (game.state != gss[0] && game.state != gss[1]) {
revert GuessingGame__UnexpectedGameState(game.state);
}
_;
}

modifier oneOfPlayers(uint32 gameId) {
Game storage game = games[gameId];
bool found = false;
for (uint8 i = 0; i < game.players.length; i++) {
for (uint8 i = 0; i < game.players.length; ++i) {
if (game.players[i] == msg.sender) {
found = true;
break;
Expand Down Expand Up @@ -70,6 +62,13 @@ contract GuessingGame is IGuessingGame, Ownable {
_;
}

modifier BidInRange(uint8 bid) {
if (bid < MIN_NUM || bid > MAX_NUM) {
revert GuessingGame__BidOutOfRange(msg.sender, bid);
}
_;
}

// Helper functions
function _updateGameState(uint32 gameId, GameState state) internal validGameId(gameId) nonEndState(gameId) {
Game storage game = games[gameId];
Expand All @@ -95,12 +94,10 @@ contract GuessingGame is IGuessingGame, Ownable {
emit NewGame(gameId, msg.sender);
}

// IMPROVE: the gameStateIn() modifier code is bad. It is restricted to take
// two params.
function joinGame(uint32 gameId) external override validGameId(gameId) gameStateEq(gameId, GameState.GameInitiated) {
Game storage game = games[gameId];
// check the player has not been added to the game
for (uint8 i = 0; i < game.players.length; i++) {
for (uint8 i = 0; i < game.players.length; ++i) {
if (game.players[i] == msg.sender) {
revert GuessingGame__PlayerAlreadyJoin(msg.sender);
}
Expand All @@ -110,9 +107,9 @@ contract GuessingGame is IGuessingGame, Ownable {
emit PlayerJoinGame(gameId, msg.sender);
}

function startRound(
function startGame(
uint32 gameId
) external override validGameId(gameId) byGameHost(gameId) gameStateIn2(gameId, [GameState.GameInitiated, GameState.RoundEnd]) {
) external override validGameId(gameId) byGameHost(gameId) gameStateEq(gameId, GameState.GameInitiated) {
_updateGameState(gameId, GameState.RoundBid);
emit GameStarted(gameId);
}
Expand All @@ -130,7 +127,7 @@ contract GuessingGame is IGuessingGame, Ownable {

// If all players have submitted bid, update game state
bool notYetBid = false;
for (uint i = 0; i < game.players.length; i++) {
for (uint i = 0; i < game.players.length; ++i) {
address p = game.players[i];
if (game.bids[round][p].bid_null_hash == bytes32(0)) {
notYetBid = true;
Expand All @@ -143,11 +140,80 @@ contract GuessingGame is IGuessingGame, Ownable {
}
}

function revealBid() {
function _verifyBidProof(bytes32 proof, uint8 bid, uint256 nullifier) internal pure returns (bool) {
/**
* TODO: verify proof
**/
proof;
bid;
nullifier;
return true;
}

function revealBid(
uint32 gameId,
bytes32 proof,
uint8 bid,
uint256 nullifier
)
external
override
validGameId(gameId)
oneOfPlayers(gameId)
gameStateEq(gameId, GameState.RoundReveal)
BidInRange(bid)
{
Game storage game = games[gameId];

// each player reveal a bid. The last player that reveal a bid will change the game state
bool proofVerified = _verifyBidProof(proof, bid, nullifier);
if (!proofVerified) {
revert GuessingGame__BidProofRejected(msg.sender, gameId, game.currentRound);
}

uint8 round = game.currentRound;
game.revelations[round][msg.sender] = bid;
emit BidRevealed(gameId, round, msg.sender);

// If all players have submitted revelation, update game state
bool notYetReveal = false;
for (uint i = 0; i < game.players.length; ++i) {
address p = game.players[i];
if (game.revelations[round][p] == 0) {
notYetReveal = true;
break;
}
}

if (!notYetReveal) {
_updateGameState(gameId, GameState.RoundEnd);
}
}

function endRound() {
// the average will be cmoputed, the winner will be determined. Update the game state.
function endRound(
uint32 gameId
) external override validGameId(gameId) byGameHost(gameId) gameStateEq(gameId, GameState.RoundEnd) {
Game storage game = games[gameId];

/**
* TODO: calc the average of all bids, determine the winnder
**/

// Assume the game host is winner for now
address roundWinner = game.players[0];

// Notice we also update the game.currentRound here
uint8 round = game.currentRound++;
++game.roundWon[roundWinner];

// update the game.state or end the game
if (game.roundWon[roundWinner] == ROUND_TO_WIN) {
game.finalWinner = roundWinner;
emit GameWinner(gameId, roundWinner);
_updateGameState(gameId, GameState.GameEnd);
} else {
emit RoundWinner(gameId, round, roundWinner);
_updateGameState(gameId, GameState.RoundBid);
}
}
}
2 changes: 1 addition & 1 deletion apps/contracts/contracts/base/Constants.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

uint8 constant MIN_NUM = 0;
uint8 constant MIN_NUM = 1;
uint8 constant MAX_NUM = 100;
uint8 constant ROUND_TO_WIN = 5;
12 changes: 10 additions & 2 deletions apps/contracts/contracts/interfaces/IGuessingGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ interface IGuessingGame {
struct Game {
// game players. The first player is the game host
address[] players;
mapping(address => uint8[]) winning;
mapping(address => uint8) roundWon;
uint8 currentRound;
GameState state;
// player bid list
mapping(uint8 => mapping(address => Bid)) bids;
mapping(uint8 => mapping(address => uint8)) revelations;
address finalWinner;
uint256 startTime;
uint256 lastUpdate;
Expand All @@ -37,17 +38,24 @@ interface IGuessingGame {
error GuessingGame__PlayerAlreadyJoin(address p);
error GuessingGame__SenderIsNotGameHost();
error GuessingGame__SenderNotOneOfPlayers();
error GuessingGame__BidProofRejected(address, uint32, uint8);
error GuessingGame__BidOutOfRange(address, uint8);

// Emitted Events
event NewGame(uint32 indexed gameId, address indexed sender);
event PlayerJoinGame(uint32 indexed gameId, address indexed sender);
event GameStarted(uint32 gameId);
event GameStateUpdated(uint32 gameId, GameState state);
event BidSubmitted(uint32 gameId, uint8 round, address sender);
event BidRevealed(uint32 gameId, uint8 round, address sender);
event RoundWinner(uint32 gameId, uint8 round, address winner);
event GameWinner(uint32 gameId, address winner);

// External Functions
function newGame() external returns (uint32 gameId);
function joinGame(uint32 gameId) external;
function startRound(uint32 gameId) external;
function startGame(uint32 gameId) external;
function submitBid(uint32 gameId, bytes32 bid_nullifier_hash, bytes32 nullifier_hash) external;
function revealBid(uint32 gameId, bytes32 proof, uint8 bid, uint256 nullifier) external;
function endRound(uint32 gameId) external;
}
20 changes: 19 additions & 1 deletion apps/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,25 @@ function getNetworks(): NetworksUserConfig {
}

const hardhatConfig: HardhatUserConfig = {
solidity: "0.8.23",
solidity: {
version: "0.8.23",
settings: {
// We hit the "Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent
// `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing
// local variables." problem. So we enable it.
// ref: https://hardhat.org/hardhat-runner/docs/reference/solidity-support#support-for-ir-based-codegen
viaIR: true
// optimizer: {
// enabled: true,
// runs: 77,
// details: {
// yulDetails: {
// optimizerSteps: "u",
// },
// },
// },
}
},
defaultNetwork: process.env.DEFAULT_NETWORK || "localhost",
networks: {
hardhat: {
Expand Down

0 comments on commit 934c8c9

Please sign in to comment.