How do i encrypt player's transaction(object)

for example i wanna make a rock- scissors- paper game, users need to call the API to make an action. let’s say there action will be one of (1, 2, 3) and we don’t want others to see it. also we wanna check the result when the game is closed, even the players are off line already. Any idea, is it necessary to use ZKP? thanks

For rock-paper-scissors, the classic way to implement this with a smart contract is to have each player post a cryptographic commitment to their move, then subsequently reveal the commitment. This does not require zero knowledge proofs or encryption. As a partial example

module example::rock_paper_scisssors {

  const ROCK: u8 = 0;
  const PAPER: u8 = 1;
  const SCISSORS: u8 = 2;

  struct Game has key {
    id: UID,
    player1: address,
    player2: address,
    // filled in by player1 upon creation
    player1_commitment: vector<u8>,
    // filled in by player2 via make_move
    player2_commitment: vector<u8>,
    // filled in by player1 after player2 makes a move
    player1_move: Option<u8>,
    // filled in by player2 after player2 makes a move
    player2_move: Option<u8>,
    // becomes true after player2 has made a move
    reveal_phase: false
  }

  // special object given to the winner for bragging rights
  struct Winner has key, store {
    id: UID
    oponent: address,
  }

  // offline, player 1 computes player1_commitment = sha3_256(random() | ROCK) (if they want to play rock). later, they will reveal both random() and ROCK
  public fun create(player1_commitment: vector<u8>, player2: address, ctx: &mut TxContext) {
    transfer::share(Game {
      id: object::new(ctx),
      player1: ctx.sender(),
      player2,
      player1_commitment,
      player2_commitment: vector[], // to be filled in by player 2 in a subsequent tx,
      player1_move: option::none(),
      player2_move: option::none()
    })
  }

  // offline, player 2 computes player2_commitment = sha3_256(random() | SCISSORS) (if they want to play scissors). later, they will reveal both random() and SCISSORS
  public fun make_move(player2_commitment: vector<u8>, game: &mut Game, ctx: &mut TxContext) {
    assert!(game.player2 == ctx.sender(),
    assert(!game.reveal_phase, EWaitingOnPlayer2); // player 2 already made their movve
    game.player2_commitment = player2_commitment;
    game.reveal_phase = true
  }

  public fun reveal(salt: vector<u8>, choice: u8, game: &mut Game, ctx: &mut TxContext) {
    assert(game.reveal_phase, EWaitingOnPlayer2); // need player 2 to make move first

    let player = ctx.sender();
    if (player == game.player1 && option::is_none(&game.player1_move)) {
      salt.push_back(choice);
      // ensure commitment matches choice
      assert!(hash::sha3_256(salt) == game.player1_commitment, EBadReveal);
      player1_move = option::some(choice);
    } else if (...) // similar logic for player 2
  }

  public fun declare_winner(game: Game): Winner {
    ... // if both players have revealed, compute winner and give them a Winner object
  }
}

This example is partial because you would typically want to build in a timeout that allows one player to win via timeout if the other player stops cooperating (e.g., if player 1 reveals first and player 2 gets sour grapes because they can see that they are going to lose). This is especially important if the game contains a betting element (e.g., locked coins inside Game).

2 Likes

hi what if this game requires every step to be revealed , even when some of the players will be offline after the first movement?

I mean if we can not ask the players to reveal by theirselves

up, anybody help? thanks

This is not currently possible–someone who knows the secret (a player, a game server, …) must reveal it.