Thimblerig game architecture: commit reveal scheme
There is a type of games where you play against an opponent hiding some information and making opponent guess it. The game outcome depends on both players actions and luck. One of widely known games of this type is thimblerig.
In this game one player hides a ball behind one of three thimbles. Second player needs to guess under which one the ball was hidden.
In real life it’s simple to make sure the fairness of the process. Hiding player can witness the revealing process while guessing player can check other thimbles after revealing the choice. However when playing by network there is a challenge for guesser to make sure that hider hasn’t changed the ball position after knowing the choice. At the same time hider can’t tell the position in advance so guesser could know it.
Commit-reveal scheme is here to help. First hider generates a big number known as salt. It’s impossible to guess or brute-force this number without knowing. In o1js this can be done by using `Field.random()`. Salt is stored persistently in the hider’s browser as it will be needed later during reveal step.
The hider calculates hash of the position concatenated with salt. Then passed to the zkApp function
const commitment = Poseidon.hash([
...UInt64.from(chosenPosition).toFields(),
salt,
]);
const tx = await client.transaction(
PublicKey.fromBase58(networkStore.address!),
() => {
thimblerigLogic.commitValue(
UInt64.from(matchQueue.activeGameId),
commitment
);
}
);
Function calculates hash of position and salt concatenated together. This hash is the public output of zkApp function and stored in the current game info so guesser knows it. By sharing hash hider determines the answer and has no options to change it in future — otherwise re-calculated hash in future wont match this hash
Now guesser knows the hash but has no idea where the ball is hidden. Without knowing the salt it’s impossible to know the hidden position. Making and sharing a proof of having some information without sharing the data is known as commitment
After choosing a supposed ball position guessing player shares the guess by calling zkApp function that updates current game state
Now hider has all the information of game outcome and ideally reveals the position and salt by calling zkApp function. It recaulculates the hash and verifies value and salt passed are correct. If data is corrupted, reveal is not confirmed and guesser got technical win after timeout if correct data is not revealed still
If hider correctly reveals salt and position, game outcome is known for anyone. Winner is determined and prize is distributed
The difficult situation here is, after knowing the guess, hider knows the game outcome while publicly it’s unknown. So he may decide not to reveal the commitment if he lost. So hider needs to be incentivized to reveal commitment regardless the game outcome. For this reason winner and loser rewards doesn’t not work in the all or nothing manner. Winner got 2/3 of the round funds while looser got only 1/3. So it’s always more profitable to reveal the commitment
Thimblerig is only one of simple examples of commit reveal scheme usage. There are more complex games utilizing this scheme like rock paper scissors or battleship. ZkNoid offers commit reveal scheme on the framework level as well as matchmaking and UI bootstrapping. If you’re interested in games development, feel free to check out our docs