zkShuffle is our implementation of the Mental Poker scheme, a framework for playing card/board game without physical cards and without a trusted third party. For example, if you build a mental poker framework for a deck of 52 cards, you can almost write any poker games such as Texas Hold'em using only solidity.
For a comprehensive overview, Nicolas Mohnblatt from Geometry Research has a very nice series of articles (part-1, part-2).
However, Geometry Research's implementation uses Starknet curve, and is quite expensive even if we simply replace the Starknet curve using BN254. In this work, we propose a new mental poker design focusing on reducing gas cost on Ethereum so that everyone can play Texas Hold'em and Hearth Stone on Ethereum!
At a high level, zkShuffle can distribute and shuffle a deck of cards privately to each individual player. And a player can reveal a single card (or a subset of cards) on her hand when she plays her hand.
Note: our construction uses Groth16 on a pairing friendly curve. Thus, we use the following notation:
: the group element in the embedded curve, i.e. Baby Jubjub
: the base field of
: the scalar field of
Suppose there are
Setup
: The mental poker scheme provides a generator shuffle_encrypt
: To shuffle a deck of cards, every player need to take her turn to call shuffle_encrypt
. A player Note: by the homomorphic property of ElGamal, one nice property of
shuffle_encrypt
is, the order of encryption is irrelevant to the result! After the shuffle and encryption of the deck,posts the produced deck as well as a zero-knowledge proof of the validity of the shuffle and encryption on chain.
decrypt
: A player decrypt_post
: A player decrypt
. After decrypt, the player post the decrypted card as well as the validity proof of decryption on chain.Let's say Alice, Bob, and Charlie want to play a poker game together. The first step is that they need to shuffle a deck of encrypted cards together. We require each player join the shuffle so that none of any subset of the players can control the sequence of shuffled cards or encryption. Here, we need to use a homomorphic encryption scheme, to ensure the sequence of encryption and decryption would not affect the final result.
To shuffle the deck, we start from a deck of open card. Then, each player takes turns calling shuffle_encrypt
to randomly shuffle the deck of card she received and apply the homomorphic encryption scheme ("add a lock" as shown in the above figure). At the end of the round, we achieve:
Let's ask two questions:
Q1. Why every player needs to join the shuffle?
Since we don't want to trust anyone. Notice, after the first player, each player only shuffle the encrypted card deck. Essentially every player contribute the randomness of the final deck and unless all players collude, the shuffle is fair.
Q2. Why do we need homomorphic encryption?
Homomorphic encryption is needed since the order of encryption and decryption is irrelavent to the end encrypt/decrypt result. We need to leverage this property in the card dealing part.
Card dealing handles 3 cases in the poker game:
Let's first look at how to deal a Card to Alice. Suppose we already have a shuffled and encrypted deck on chain. And now we are dealing the first card on the deck to Charlie. Below is the sequence of actons:
decrypt_post
on the first card, which calls decrypt
the first card and writes it back on chaindecrypt_post
on the card (notice, now this is the card Bob already calls decrypt_post
), and write it back on chain.decrypt
(without writing it back on chain, otherwise everyone knows).Now, suppose Alice wants to play the card just dealt to her from her hand. She reveals the card and uses zero-knowledge proof to show that this is indeed the card she decrypted from the encrypted card given to her.
And also dealing community cards is simply a full round of decrypt
and the last person needs to reveal the card on chain!
Let's also ask three questions about dealing:
Q1. When dealing card to Alice, what prevents other players or anyone else from seeing Alice's card?
For the entire dealing sequence, only Alice can see the plain text after her final decrypt
. Through the process, neither Bob, Charlie, nor blockchain validators can see her card.
Q2. Does the decrypt
sequence have to be Bob->Charlie->Alice ?
No. Thanks to the homomorhpic encryption scheme we use, as long as Alice is the last one to decrypt the card, we are good!
Q3. When Alice plays her hand, what prevents her from cheating (a.k.a. playing a card that doesn't belong to her)?
That is why when Alice plays her hand, she cannot just reveal her card, she needs to submit a zero-knowledge proof to show the validity of her decryption on chain as well. We will get into details about when and where SNARK is needed next.
Both shuffle_encrypt
and decrypt_post
require the player to generate a zero-knowledge proof and submit the zero-knowledge proof together with the result.
In shuffle_encrypt
, a zero-knowledge proof is needed to prove the validity of the shuffle and encryption. This is required to guarantee the soundness of the shuffle, for example, people don't put random content in the encrypted cards so that nothing can be decrypted. For not leaking shuffle order on chain, the proof generated in shuffle_encrypt
needs to be strictly zero-knowledge.
In decrypt_post
, a zero-knowledge proof is needed to prove the validity of the decryption. This prevents from the player posting wrong decryption results intentionally to gain an advantage in the game. The proof generated in decrypt_post
needs to be zero-knowledge for not leaking player's secret key.
Given a randomly selected generator
This step has the following signature:
The output is ciphertext
This step has the following signature:
The output is message
A matrix
shuffle_encrypt
circuitFor a poker game, there is a pre-defined number of cards
Given
the prover proves the knowledge of
such that
Note:
means El Gamal encryption with individual .
We implement the circuit in Circom, the total R1CS constraint count is about
decrypt_post
circuitSuppose a prover wants to decrypt the
Given
the prover proves the knowledge of
such that
We program the circuit in Circom, the decrypt_post
circuit consists of