Comment on page
☝
1v1
The first set of games will be 1 v 1, simultaneous games.
- Two players are paired together: Alice and Bob
- Each player two options
Cheat
orCooperate
- If both players
Cooperate
then both players gets +2 tokens - If both players
Cheat
then both players get 0 tokens - If Alice
Cheats
and BobCooperates
then Alice gets +3 tokens and Bob loses -1 token.
Text | Cooperate | Cheat |
---|---|---|
Cooperate | +2, +2 | -1, +3 |
Cheat | +3, -1 | 0, 0 |
- 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
- p2Address - Player 2 address
- Latest Round - Number of games aka rounds
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 |
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
// 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 blockchain is designed for data integrity. The blockchain is not designed for data queries. Each game will be indexed by The Graph.
For any disputes or automatic reveals:
Last modified 1yr ago