0sum.io
Search
K
Comment on page

1v1

Overview

The first set of games will be 1 v 1, simultaneous games.

Rules

  • Two players are paired together: Alice and Bob
  • Each player two options Cheat or Cooperate
  • If both players Cooperate then both players gets +2 tokens
  • If both players Cheat then both players get 0 tokens
  • If Alice Cheats and Bob Cooperates then Alice gets +3 tokens and Bob loses -1 token.
Text
Cooperate
Cheat
Cooperate
+2, +2
-1, +3
Cheat
+3, -1
0, 0

Definitions

  • Player - Each player (address) has 3 properties
    • Tokens - Token balance
    • Cheated - Number of times player cheated
    • Cooperated - Number of times player cooperated
  • Action - Player has 3 actions
    • None - Player took no action
    • Cheat - Player cheated
    • Cooperate - Player cooperated
  • Game - Each game is recorded
    • isDone - Check if game is done
    • p1Address - Player 1 address
    • p1Hash - Player 1 committed hash (unrevealed action)
    • p1Action - Player 1 revealed commit (revealed action)
    • p2Address - Player 2 address
    • p2Hash - Player 2 committed hash (unrevealed action)
    • p2Action - Player 2 revealed commit (revealed action)
    • expiration - Latest time for players to reveal, can slash if no reveal
  • Latest Round - Number of games aka rounds

Player History

Players are able to see their opponents balance and actions from previous games
Player1
tokens
cheated
cooperated
Game 3
16
1
0
Game 2
13
1
0
Game 1
13
1
0
Game 0
10
1
0

Commit-Reveal

Since all blockchain transactions are public and sequential, the first mover is at a disadvantage. For example, if Player1 submits first, Player2 can see that action and made decisions accordingly.
Both players commit a hidden value on chain. These values cannot be changed. Once both values are committed, both players can then reveal their hidden value.
On a technical level
  • Player 1 choses action, p1Action = cooperate off chain
  • Player 1 hashes the action hash(p1Action) = p1Hash and submits it on chain
  • Player 2 choses action, p2Action = cheat off chain
  • Player 2 hashes the action hash(p2Action) = p2Hash and submits it on chain
  • Now that both hashes are on chain, each player can reveal their action
  • Player 1 reveals cooperate
  • Player 2 reveals cheat

Solidity

// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SignedSafeMath.sol";
contract GameOfTrust {
// state of each player
mapping (address => Player) public player;
struct Player {
int256 tokens; // token balance
uint256 cheated; // number of times player cheated
uint256 cooperated; // number of times player cooperated
}
// player actions
enum Action {
None, // player did not reveal their action
Cheat, // player cheated
Cooperate // player cooperated
}
// state of each game
mapping (uint256 => Game) public game;
struct Game {
address p1Address; // player 1 address
bytes32 p1Hash; // player 1 committed hash (unrevealed action)
Action p1Action; // player 1 revealed commit (revealed action)
address p2Address; // player 2 address
bytes32 p2Hash; // player 2 commited hash (unrevealed action)
Action p2Action; // player 2 revealed commit (revealed action)
uint256 expiration; // time for players to reveal, can slash() if no reveal
}
// number of rounds in game
uint256 public latestRound = 0;
function getHash(bytes32 _key, Action _action) public pure returns(bytes32) {
// _key = 0x0000000000000000000000000000000000000000000000000000000000000001
// _action = 1
// returns 0xa27e787d91efd69625bc076a83f171734357bdc4ce1617551ee1cdc7fb10c4e1
return keccak256(abi.encodePacked(_key, _action));
}
function join() public {
// if player 1 address is empty
if (game[latestRound].p1Address == address(0x0)) {
// add player 1 address
game[latestRound].p1Address = msg.sender;
}
// if player 2 address is empty and is not player 1
if (game[latestRound].p2Address == address(0x0) && game[latestRound].p1Address == msg.sender) {
// add player 2 address
game[latestRound].p2Address = msg.sender;
}
// if both players joined
if (game[latestRound].p1Address != address(0x0) && game[latestRound].p2Address != address(0x0)) {
// set expiration
game[latestRound].expiration = block.timestamp + 24 hours;
// bump latest round
latestRound = SafeMath.add(latestRound, 1);
}
}
function commit(uint256 _round, bytes32 _hash) public {
require(game[_round].p1Address == msg.sender || game[_round].p2Address == msg.sender, "invalid player");
// if player 1
if (game[_round].p1Address == msg.sender) {
// add player 1 commit hash
game[_round].p1Hash = _hash;
}
// if player 2
if (game[_round].p2Address == msg.sender) {
// add player 2 commit hash
game[_round].p2Hash = _hash;
}
}
function reveal(uint256 _round, bytes32 _key, Action _action) public {
require(game[_round].p1Address == msg.sender || game[_round].p2Address == msg.sender, "invalid player");
require(getHash(_key, _action) == game[_round].p1Hash || getHash(_key, _action) == game[_round].p2Hash, "invalid key");
// if player 1 reveals action
if (game[_round].p1Address == msg.sender && getHash(_key, _action) == game[_round].p1Hash) {
// save player 1 action
game[_round].p1Action = _action;
}
// if player 2 reveals action
if (game[_round].p1Address == msg.sender && getHash(_key, _action) == game[_round].p2Hash) {
// save player 2 action
game[_round].p2Action = _action;
}
// if both players reveal
if (game[_round].p1Action != Action.None && game[_round].p2Action != Action.None) {
// reward round
reward(_round);
}
}
function reward(uint256 _round) private {
// if both players cooperate
if (game[_round].p1Action == Action.Cooperate && game[_round].p2Action == Action.Cooperate) {
// update player stats
player[game[_round].p1Address].cooperated = SafeMath.add(player[game[_round].p1Address].cooperated, 1);
player[game[_round].p2Address].cooperated = SafeMath.add(player[game[_round].p2Address].cooperated, 1);
// +2/+2
player[game[_round].p1Address].tokens = SignedSafeMath.add(player[game[_round].p1Address].tokens, 2);
player[game[_round].p2Address].tokens = SignedSafeMath.add(player[game[_round].p2Address].tokens, 2);
}
// if both players cheat
if (game[_round].p1Action == Action.Cheat && game[_round].p2Action == Action.Cheat) {
// update player stats
player[game[_round].p1Address].cheated = SafeMath.add(player[game[_round].p1Address].cheated, 1);
player[game[_round].p2Address].cheated = SafeMath.add(player[game[_round].p2Address].cheated, 1);
// 0/0 no token update
}
// if player 1 cheats and player 2 cooperates
if (game[_round].p1Action == Action.Cheat && game[_round].p2Action == Action.Cooperate) {
// update player stats
player[game[_round].p1Address].cheated = SafeMath.add(player[game[_round].p1Address].cheated, 1);
player[game[_round].p2Address].cooperated = SafeMath.add(player[game[_round].p2Address].cooperated, 1);
// +3/-1
player[game[_round].p1Address].tokens = SignedSafeMath.add(player[game[_round].p1Address].tokens, 3);
player[game[_round].p2Address].tokens = SignedSafeMath.add(player[game[_round].p2Address].tokens, -1);
}
// if player 1 cooperates and player 2 cheats
if (game[_round].p1Action == Action.Cooperate && game[_round].p2Action == Action.Cheat) {
// update player stats
player[game[_round].p1Address].cooperated = SafeMath.add(player[game[_round].p1Address].cooperated, 1);
player[game[_round].p2Address].cheated = SafeMath.add(player[game[_round].p2Address].cheated, 1);
// -1/+3
player[game[_round].p1Address].tokens = SignedSafeMath.add(player[game[_round].p1Address].tokens, -1);
player[game[_round].p2Address].tokens = SignedSafeMath.add(player[game[_round].p2Address].tokens, 3);
}
}
function slash(uint256 _round) public {
require(block.timestamp >= game[_round].expiration, "game has not expired");
// if player 1 revealed and player 2 did not reveal
if (game[_round].p1Action != Action.None && game[_round].p2Action == Action.None) {
// give token to player 1
player[game[_round].p1Address].tokens = SignedSafeMath.add(player[game[_round].p1Address].tokens, 1);
}
// if player 1 did not reveal and player 2 revealed
if (game[_round].p1Action == Action.None && game[_round].p2Action != Action.None) {
// give token to player 2
player[game[_round].p2Address].tokens = SignedSafeMath.add(player[game[_round].p2Address].tokens, 1);
}
// give token to slasher
player[msg.sender].tokens = SignedSafeMath.add(player[msg.sender].tokens, 1);
}
}

The Graph

The blockchain is designed for data integrity. The blockchain is not designed for data queries. Each game will be indexed by The Graph.

Gelato

For any disputes or automatic reveals:
Last modified 1yr ago