# zkHoldem

## Introduction

[zkHoldem](https://www.zkholdem.xyz/) is a Texas Hold'em game built on [zkShuffle](https://zkholdem.xyz/wp-content/themes/zkholdem-theme/zkshuffle.pdf) and launched on [Manta Network](https://manta.network/).

The subtitle of zkShuffle is **"Mental Poker on SNARK for Ethereum."** The term *Mental Poker* was introduced in a [1979 paper](https://people.csail.mit.edu/rivest/pubs/SRA81.pdf) by Adi Shamir, Ron Rivest, and Leonard Adleman. It explores whether a fair poker game can be played online without physical cards while ensuring the following three principles:

1. Hide the card values from all players
2. Ensure that the cards are dealt correctly, with fair randomness
3. Reveal a card to a specific player or group of players

## Background

Please read [ElGamal Encryption](https://fractalyze.gitbook.io/intro/primitives/encryption-scheme/elgamal-encryption) before continuing on.

## Protocol Explanation

### Overview of zkShuffle

zkShuffle is a protocol that distributes and shuffles cards among players. It primarily follows the [*Barnett and Smart*](https://archive.cone.informatik.uni-freiburg.de/teaching/teamprojekt/dog-w10/literature/mentalpoker-revisited.pdf) structure, which has also been improved and implemented by Geometry.

The key **novelty** of zkHoldem compared to Geometry's implementation is that it utilizes **Groth16** to implement the **shuffle argument** more efficiently. For further details, interested readers can refer to the [Geometry blog](https://geometry.xyz/notebook/mental-poker-in-the-age-of-snarks-part-1).

### **Modified ElGamal Encryption**

#### $$\mathsf{ModifiedElGamal.Setup}() \rightarrow (\mathsf{sk}, \mathsf{pk})$$

* Randomly select a **secret key** $$\mathsf{sk}$$ in the range $$1 \leq \mathsf{sk} < q-1$$.
* Compute the **public key** $$\mathsf{pk}$$ as follows:

$$
\mathsf{pk} = \mathsf{sk} \cdot G
$$

where $$G$$ is a generator of an additive group of a large prime size.

#### $$\mathsf{ModifiedElGamal.Encrypt}(m\_1: \mathbb{G}, m\_2: \mathbb{G}, \mathsf{pk}, r: \mathbb{F}) \rightarrow (c\_1: \mathbb{G}, c\_2: \mathbb{G})$$

* When called for the first time, $$m\_1 = 0$$ and $$m\_2 = m$$, the message. This value is used for nested encryption.
* A random scalar $$r$$ where $$1 \leq r < q-1$$ is used to determine the shared secret $$s$$.

$$
s = r \cdot \mathsf{pk}
$$

* Generate the ciphertext $$(c\_1, c\_2)$$ as follows:

$$
c\_1 = m\_1 + r\cdot G, \quad c\_2 = m\_2 + r \cdot \mathsf{pk}
$$

#### $$\mathsf{ModifiedElGamal.Decrypt}(c\_1: \mathbb{G}, c\_2: \mathbb{G}, \mathsf{sk}: \mathbb{F}, r: \mathbb{F}) \rightarrow m: \mathbb{G}$$

* $$c\_1$$ is computed using the value $$r$$ that was used when calling $$\mathsf{ModifiedElGamal.Encrypt}$$. Note that this $$c\_1$$ computed individually by each party is different from the iterative $$c\_1$$ from $$\mathsf{.Encrypt}$$.

$$
c\_1 = r \cdot G
$$

* Compute the plaintext $$m$$ as follows:

$$
m =  c\_2 - \mathsf{sk} \cdot c\_1
$$

### **Properties of Modified ElGamal**

Consider a scenario where Alice, Bob, and Carol each have private keys $$\mathsf{sk}\_0, \mathsf{sk}\_1, \mathsf{sk}\_2$$ and their corresponding public keys $$\mathsf{pk}\_0, \mathsf{pk}\_1, \mathsf{pk}\_2$$​. Now, let's examine the encryption process for a card $$m$$.

1. **Alice encrypts using** $$r\_0$$$$(m\_1 = 0)$$**:**

$$
\mathsf{ModifiedElGamal.Encrypt}(0, m, \mathsf{pk}\_0, r\_0) \rightarrow (r\_0 \cdot G, m + r\_0 \cdot \mathsf{pk}\_0)
$$

2. **Bob encrypts using** $$r\_1$$**​:**

$$
\mathsf{ModifiedElGamal.Encrypt}(r\_0 \cdot G, m + r\_0 \cdot \mathsf{pk}\_0, \mathsf{pk}\_1, r\_1) \rightarrow ((r\_0 + r\_1) \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_1 \cdot \mathsf{pk}\_1)
$$

3. **Carol encrypts using** $$r\_2$$**​:**

$$
\mathsf{ModifiedElGamal.Encrypt}((r\_0 + r\_1) \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_1 \cdot \mathsf{pk}\_1, \mathsf{pk}\_2, r\_2) \rightarrow ((r\_0 + r\_1 + r\_2) \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_1 \cdot \mathsf{pk}\_1 + r\_2 \cdot \mathsf{pk}\_2)
$$

After this process, the final ciphertext $$\bm{c}$$ is generated as follows:

$$
\bm{c} = ((r\_0 + r\_1 + r\_2) \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_1 \cdot \mathsf{pk}\_1 + r\_2 \cdot \mathsf{pk}\_2)
$$

At this stage, Alice, Bob, and Carol can **decrypt in any order** to retrieve the original message $$m$$. Suppose they decrypt in the order **Bob → Carol → Alice**:

1. **Bob decrypts using** $$r\_1$$**​:**

$$
\mathsf{ModifiedElGamal.Decrypt}(r\_1 \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_1 \cdot \mathsf{pk}\_1 + r\_2 \cdot \mathsf{pk}\_2, \mathsf{sk}\_1, r\_1) \rightarrow m +  r\_0 \cdot \mathsf{pk}\_0 + r\_2 \cdot \mathsf{pk}\_2
$$

2. **Carol decrypts using** $$r\_2$$**​:**

$$
\mathsf{ModifiedElGamal.Decrypt}(r\_2 \cdot G, m + r\_0 \cdot \mathsf{pk}\_0 + r\_2 \cdot \mathsf{pk}\_2, \mathsf{sk}\_2, r\_2) \rightarrow m + r\_0 \cdot \mathsf{pk}\_0
$$

3. **Alice decrypts using** $$r\_0$$**​:**

$$
\mathsf{ModifiedElGamal.Decrypt}(r\_0 \cdot G, m + r\_0 \cdot \mathsf{pk}\_0, \mathsf{sk}\_0, r\_0) \rightarrow m
$$

#### **Advantages of Modified ElGamal**

One of the key features of Modified ElGamal is its **order-independent decryption** property.

This is **particularly useful in blockchain environments**, where:

* Decryption results need to be submitted as **on-chain transactions (tx)**.
* The ordering of transactions within a block is unpredictable.

Since decryption can be performed in **any order**, this significantly enhances **the usability of the protocol in decentralized systems**.

### **Shuffling**

In Texas Hold'em, a deck of **52 cards** must be shuffled, which needs to be mathematically represented. For example, suppose we start with a set of four cards:

$$
\bm{C\_0} = (c\_0, c\_1, c\_2, c\_3)
$$

If Alice wants to shuffle them into the following order:

$$
\bm{C\_1} = (c\_0, c\_2, c\_1, c\_3)
$$

how can we express this mathematically?

This can be represented using a [**permutation matrix**](https://en.wikipedia.org/wiki/Permutation_matrix) as a matrix multiplication:

$$
\bm{C\_1} =\begin{bmatrix} c\_0 \c\_2 \c\_1 \c\_3 \\\end{bmatrix} =\begin{bmatrix} 1 & 0 & 0 & 0  \0 & 0 & 1 & 0  \0 & 1 & 0 & 0  \0 & 0 & 0 & 1  \\\end{bmatrix} \cdot\begin{bmatrix} c\_0 \c\_1 \c\_2 \c\_3 \\\end{bmatrix}
$$

#### **Conditions for a Permutation Matrix**

A matrix $$A$$ is a **permutation matrix** if it satisfies the following three conditions:

1. $$A$$ **must be a square matrix.**

$$
\mathsf{IsSquare}(A) := M.\mathsf{rows} \stackrel{?}{=} M.\mathsf{cols}
$$

2. **All elements of** $$A$$ **must be either 0 or 1.**

$$
\mathsf{HasOnlyBinaryElements}(A) := \bigwedge\_{i=0}^{\mathsf{rows}-1} \bigwedge\_{j=0}^{\mathsf{cols}-1} \mathsf{IsBinary}(A\_{i,j})
$$

The function $$\mathsf{IsBinary}$$ is defined as:

$$
\mathsf{IsBinary}(A\_{i,j}) := A\_{i, j} \stackrel{?}{=} 0 \lor A\_{i, j} \stackrel{?}{=} 1
$$

3. **Each row and each column of** $$A$$ **must sum to 1.**

$$
\mathsf{HasValidRowAndColSums}(A) := \bigwedge\_{i=0}^{\mathsf{rows}-1} \mathsf{IsRowSumOne}(A, i) \land \bigwedge\_{j=0}^{\mathsf{cols}-1} \mathsf{IsColSumOne}(A, j)
$$

The function $$\mathsf{IsRowSumOne}$$ is defined as:

$$
\mathsf{IsRowSumOne}(A, i) := \sum\_{j=0}^{\mathsf{cols}-1}A\_{i,j} \stackrel{?}{=} 1
$$

The function $$\mathsf{IsColSumOne}$$ is defined as:

$$
\mathsf{IsColSumOne}(A, j) := \sum\_{i=0}^{\mathsf{rows}-1}A\_{i,j} \stackrel{?}{=} 1
$$

Thus, the function $$\mathsf{IsPermutationMatrix}$$ can be defined as:

$$
\mathsf{IsPermutationMatrix}(A) := \mathsf{IsSquare}(A) \land \mathsf{HasOnlyBinaryElements}(A) \land \mathsf{HasValidRowAndColSums}(A)
$$

### **zkShuffle Scheme**

Consider a scenario where $$k$$ players $$\bm{P} = {p\_0, \dots, p\_{k-1}}$$ participate, and there are $$n$$ cards in the game. Each player $$p\_i$$ is responsible for shuffling an encrypted deck $$\bm{C\_i} = (c\_{i,0}, \dots, c\_{i,n-1}) \in (\mathbb{G} \times \mathbb{G})^n$$. Here, $$n$$ is $$52$$.

Where:

* $$\mathbb{G}$$ is an **elliptic curve group**, such as Baby Jubjub.
* $$\mathbb{F}$$ is the **scalar field** of the elliptic curve.
* $$G$$ is the **generator** of the elliptic curve group.

All cards are given publicly known values, for example:

$$
\bm{C\_0} = ((0, 0), (0, G), (0, 2 \cdot G), \dots, (0, (n-1) \cdot G))
$$

Thus, for $$0 \leq j < n$$, if $$j \cdot G$$ is given, the corresponding card can be determined.

#### $$\mathsf{zkShuffle.Setup}$$

* Each player $$p\_i$$ randomly generates a **secret key** $$\mathsf{sk}\_i \in \mathbb{F}$$.
* The **public key** $$\mathsf{pk}\_i \in \mathbb{G}$$ is computed as follows:

$$
\mathsf{pk}\_i = \mathsf{sk}\_i \cdot G
$$

#### $$\mathsf{zkShuffle.ShuffleEncrypt}$$

<figure><img src="https://755218234-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Frwz1ZAZJtK5FHz4Y1esA%2Fuploads%2FqUTFVFRV2k9p668coxRN%2Fimage.png?alt=media&#x26;token=efb937a8-9dfb-4cd5-9999-543cfafdbb4e" alt=""><figcaption></figcaption></figure>

* Player $$p\_i$$ generates a **random permutation matrix** $$\bm{A\_i} \in \mathbb{F}^{n\times n}$$.
* Upon receiving deck $$\bm{C\_i} = (c\_{i,0}, \dots, c\_{i,n-1})$$ from player $$p\_{i-1}$$, the shuffled deck $$\bm{S\_{i+1}} = (s\_{i+1, 0}, \dots , s\_{i+1, n-1})$$ is computed as follows:

$$
\bm{S\_{i+1}} = \bm{A\_i} \cdot \bm{C\_i}
$$

* Player $$p\_i$$ generates random values $$r\_{i,j} \in \mathbb{F}$$ (for $$0 \le j < n$$).
* Player $$p\_i$$ encrypts the shuffled deck $$\bm{S\_{i+1}}$$ into $$\bm{C\_{i+1}}$$ where each card is calculated as follows (for $$0 \le j < n$$):

$$
c\_{i+1,j} = \mathsf{ModifiedElGamal.Encrypt}(s\_{i+1,j}\[0], s\_{i+1,j}\[1], \mathsf{pk}*i, r*{i,j})
$$

* Player $$p\_i$$ generates a **ZK-proof** for the following circuit and submits it along with $$\bm{C}\_{i+1}$$ in a transaction:

$$
C\_1(x: { \bm{C\_i}, \mathsf{pk}*i }, w: { \bm{A\_i}, \bm{S*{i+1}, \bm{r\_i}} }): \\
\mathsf{IsPermutationMatrix}(\bm{A}*i) \land \bm{S*{i+1}} \stackrel{?}= \bm{A\_i} \cdot \bm{C\_i} \land \bigwedge\_{j=0}^{51}\left( c\_{i+1, j}\stackrel{?}=\mathsf{ModifiedElGamal.Encrypt}(s\_{i+1,j}\[0], s\_{i+1,j}\[1], \mathsf{pk}*i, r*{i,j})\right)
$$

#### $$\mathsf{zkShuffle.Decrypt}$$

<figure><img src="https://755218234-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Frwz1ZAZJtK5FHz4Y1esA%2Fuploads%2FA0EQYnLksRfRZoI8INj7%2Fimage.png?alt=media&#x26;token=b89089d4-5071-499f-ab68-5d13caea4fb4" alt=""><figcaption></figcaption></figure>

* Each player $$p\_i$$ decrypts a received encrypted card $$c: \mathbb{G} \times \mathbb{G}$$ to obtain $$m: \mathbb{G}$$ as follows (The reason shuffling does not need to be considered during decryption is that if we assume the cards were shuffled using $$\mathsf{zkShuffle.ShuffleEncrypt}$$, the final recovered message ($$m$$) will always be one of the elements in $$\bm{C\_0}$$.):

$$
m = \mathsf{ModifiedElGamal.Decrypt}(c\[0], c\[1], \mathsf{sk}\_i, r)
$$

#### $$\mathsf{zkShuffle.DecryptPost}$$

* Each player $$p\_i$$ decrypts an encrypted card $$c: \mathbb{G} \times \mathbb{G}$$ to obtain $$m: \mathbb{G}$$:

$$
m = \mathsf{ModifiedElGamal.Decrypt}(c\[0], c\[1], \mathsf{sk}\_i, r)
$$

* The player generates a **ZK-proof** for the following circuit and submits it along with $$m$$ in a transaction:

$$
C\_2(x: { c, m }, w: { \mathsf{sk}\_i , r}): \\
m \stackrel{?}=\mathsf{ModifiedElGamal.Decrypt}(c\[0], c\[1], \mathsf{sk}\_i, r)
$$

* Since the contract already has the shuffled encrypted cards, it knows which card is at the top of the deck and which card needs to be decrypted. It verifies whether the public input corresponds to the correct card for decryption.

### **Game Flow**

1. **Shuffling Cards**
   1. Cards are given publicly known values.
   2. All players shuffle the cards using $$\mathsf{zkShuffle.ShuffleEncrypt}$$.
2. **Distributing Two Cards to Each Player**
   1. Cards are dealt from the top of the deck to players in a predetermined order.
   2. **For all dealt cards that do not belong to them, players call** $$\mathsf{zkShuffle.DecryptPost}$$.
   3. After step **b** is finished by every player, **players reveal their own cards to themselves** with $$\mathsf{zkShuffle.Decrypt}$$.
3. **Revealing Three Community Cards**
   1. The top three cards from the deck are placed on the table.
   2. All players call $$\mathsf{zkShuffle.DecryptPost}$$ for these cards, revealing them.
4. **Revealing Additional Community Cards (Two Rounds)**
   1. One additional card from the top of the deck is placed on the table in each round.
   2. All players call $$\mathsf{zkShuffle.DecryptPost}$$ for these cards, revealing them.
5. **Revealing Player Cards**
   1. Players who remain until the end reveal their cards using $$\mathsf{zkShuffle.DecryptPost}$$.

### **How Are the Three Principles Ensured?**

1. **Hide the card values from all players.**
   1. Cards are encrypted using $$\mathsf{zkShuffle.ShuffleEncrypt}$$. Therefore, unless all players participate in decryption, they cannot access the original card value.
2. **Ensure that the cards are dealt correctly, with fair randomness.**
   1. Before the game starts, all players contribute randomness to shuffle the cards. Even if some players collude, the deck remains randomly shuffled.
   2. $$\mathsf{zkShuffle.ShuffleEncrypt}$$ ensures that all players have encrypted the shuffled cards according to the shuffling rules using ZK.
   3. From the shuffle ZK proof, the contract knows the order of the deck.
   4. If a player attempts to decrypt a card that is not at "the top of the deck", the contract will reject the request.
3. **Reveal a card to a specific player or group of players.**
   1. **Revealing to one player**
      1. A player's card is revealed to them when calling $$\mathsf{zkShuffle.Decrypt}$$ after all other players have run $$\mathsf{zkShuffle.DecryptPost}$$.
      2. Since $$\mathsf{zkShuffle.Decrypt}$$ does not submit the result to the contract, other players cannot see their cards.
   2. **Revealing to all players**
      1. Once every player calls $$\mathsf{zkShuffle.DecryptPost}$$ on a card, it is revealed.

## **Conclusion**

The **Circom code** for zkHoldem is private, but according to the paper:

* $$\mathsf{zkShuffle.DecryptPost}$$ includes **3,500 constraints**.
* $$\mathsf{zkShuffle.ShuffleEncrypt}$$ includes **170,000 constraints**.

Using **ZK for online Texas Hold'em** is an exciting concept.

However, there are some limitations:

1. **Receiving a single card requires multiple interactions with other players for decryption.**
2. **Even if a player folds, they must stay online to complete the decryption process.**

These challenges highlight key areas for improvement in real-world applications.

## References

* <https://zkholdem.xyz/wp-content/themes/zkholdem-theme/zkshuffle.pdf>
* <https://geometry.xyz/notebook/mental-poker-in-the-age-of-snarks-part-1>
* <https://geometry.xyz/notebook/mental-poker-in-the-age-of-snarks-part-2>

> Written by [Ryan Kim](https://app.gitbook.com/u/cPk8gft4tSd0Obi6ARBfoQ16SqG2 "mention") of Fractalyze
