This is an implementation of RPS for Enigma. It eliminates the need for a commit-reveal process, and is completely decentralized.
Peter Phillips recently built an implementation of a traditional Rock, Paper, Scissors game for use with Enigma and Ethereum. Go check it out! We’ll describe how this game works, discuss some issues and future modifications (timing, winner selection), as well as suggest new applications that make use of the same basic concepts. In doing this, we’ll demonstrate how a core mechanic (secret game state) can be an important key to a wide range of applications.
Rock, Paper, Scissors — Shoot!
Rock, Paper, Scissors (RPS) is a simple game for two people. If you’ve never played it, here’s how gameplay works.
Players have just one choice to make: to play Rock, Paper, or Scissors. Players simultaneously pick their object, make the sign associated with it (a fist, scissors from first two fingers, or a flat hand), and either Player 1 wins, Player 2 wins, or there is a draw. A winner is chosen in the following manner:
- Rock beats Scissors
- Scissors beats Paper
- Paper beats Rock
- If both players play the same object, the game is a draw
Most games are played as “best out of three” or “best out of five” rounds. This example shows only one round of play (there are additional challenges when requiring more rounds, which we will discuss later).
User-flow for this demo:
- Player 1 creates a game, picks a bet, and selects an object to play.
- The UI is update to reflect an in-progress game that could be joined by a second player.
- The second player selects a game, matches the bet (no option to raise), and selects her object.
- After both participants have selected their object and placed their bets, the computation executes and compares selected objects to find the winner.
- The winner is returned, and awarded the pot.
- If there is no winner (a draw), bets are respectively returned
- Users can withdraw their funds.
For secret contracts, we have functions called callable and callback . These are the functions that communicate directly with the Enigma network. (For a more in-depth discussion, see our Getting Started guide).
function calculateWinner(uint256 _gameID, address _players, string _move1, string _move2)
Here, the Callable function is
calculateWinner which takes in encrypted values gameID, Players, move1, and move2. These values are encrypted client-side using the Enigma-JS client with the public key of the enclave.
calculateWinner evaluates the winner based on each player’s pick according to the rules of RPS. If play results in a draw,
address(0) is returned which will initiate funds to be respectively returned to the players.
function setWinner(uint256 _gameID, address _address)
This public function is called by the worker, a randomly selected node that executes computations in the Enigma network. Note that we expect the address returned by the callable (must be identically named, in this case
Feel free to dig into the well-commented code (thumbs up to Peter) for more detail on this implementation.
Comparisons and Reflections
We looked at a few other implementations of decentralized RPS for comparison. First, we looked at a demo made for Raiden (which is awesome, more about it can be found here). There are a few key differences between Peter’s “secret RPS” implementation and the Raiden rock-paper-scissors demo.
- Players pick a “player”, and then they pick a move. (for example, Darth Vader + Scissors). These are essentially “votes” for a particular move.
- The hashed selection is sent along with a payment (using Raiden) to a “game guardian”.
- A timer (drawn from the game server) is used to determine when the game ends.
- When the game ends, the unencrypted data is submitted by the game guardian to the game server, where the winner is calculated and result is returned to the game guardian.
- The game guardian instructs the Raiden nodes to distribute funds accordingly.
These choices are intended to showcase Raiden’s payment channels — hence the “voting” on an avatar and a choice rather than picking individual moves in a 1v1 game. However, in production this may leave such an approach open to sybil and whale manipulation. Peter’s implementation allows any player to start or join a specific game, rather than one all-encompassing game.
RPS could also be implemented in a straightforward smart contract on Ethereum, where the flow would be:
- Players pick their moves, hash the move data and submit it to the contract.
- After play is concluded, players reveal their hashed data
This design pattern, known as “commit-reveal”, is also used in voting to prevent vote-memeing. Unfortunately, it really slows down the pace of play. See this cool example from 2+ years ago, which demonstrates some of the intricacies and drawbacks of such periodization, as well as this more recent effort. Both of these saw limited usage due to usability — note in particular the 24 hr waiting period in the more recent example.
Two things come up from these examples: timing and winner calculation .
In single-round play, we think Secret RPS has a distinct UX advantage over these other two implementations.
However, it would be awesome to enable best of 3 or best of 5 round-based play, letting users develop more complex strategies. Without the ability to tell time inside the secret contract (which our initial release lacks), this leaves Peter’s implementation open to the dropped-player problem across multiple rounds.
For example, say Alice and Bob are playing. Alice wins the first round, and Bob suddenly has his laptop with his private keys stolen. There’s no way for the game to “time out” and for Alice to retrieve her funds (or Bob for that matter. Sorry to do this to you Bob!). This is something we hope to address soon as it appears in multiple use-cases, especially for gaming. In a single-round (which is what the code is for right now), you can always play yourself to get the funds back if no counterparty accepts your game. This UX isn’t ideal, but adding a start time to the game struct, and then check for elapsed time in the
withdraw() function would be straightforward to implement.
In Secret RPS, the winner is calculated within the Enigma Node immediately when both players have made their respective moves. Users can withdraw their funds anytime after this occurs with the
withdraw() function. This is in contrast to the “game guardian” in the Raiden example, where a server external to the network performs the computation and returns instruction to the Raiden nodes. For gaming implementations, this design may present both regulatory risk and other liabilities.
Wrapping it up
Usability is incredibly important, and we’re excited to contribute to improving usability for dApps.
We’re looking forward to building on what we’ve learned here from Peter’s implementation, and we’re also looking forward to moving this demo forward into production as we approach mainnet. What other kinds of dApps can be built using similar mechanisms: poker, dice, on-chain CryptoKitty battles… Let us know if you’re working on anything like this or would like to!