--- tags: mental_poker --- # Geometry presents: a zero knowledge library for Mental Poker (and all card games) In this short document, we present our library for trustless, decentralised card games. We keep the cryptography to a minimum; an in-depth article will follow. Why call it *Mental Poker*? *[Mental Poker](https://en.wikipedia.org/wiki/Mental_poker)* is a cryptographic problem that was first formulated in 1979 [1]. Simply put, a group of players that do not trust each other want to play a game of poker over a network. We aim to devise a protocol in which: (i) there are no physical cards, we use digitally transmitted messages instead and (ii) we must prevent cheating. As you may suspect, a solution to this *Mental Poker* problem can also be applied to any card game. **Agenda**: - Peek at the library's function calls - A round of Texas Hold'Em - A few technical notes - Contact details ## Our Library Our library allows to play any card game and is based on a paper by Barnett and Smart [2] with improved zero knowledge (zk) arguments based on [3]. The core features are listed below. - **Define custom cards**: game developers can define their own cards. - **Hide card values**: *every* player performs a secret computation locally - **Shuffle the deck**: *every* player performs a secret computation locally - **Reveal cards** to individuals or groups: *some* players perform a secret computation locally - **Prevent cheating**: each secret computation is proven in zero knowledge. Verification can be performed by players, an incentivised off-chain server or an on-chain smart contract. We will briefly discuss the operations that the library enables and will then showcase how we can use these to play a round of Texas Hold'Em. If you are happy with a high-level view only, I invite you to read each header and skip directly to the indented sections labeled "a helpful analogy". *Note: we will use simplified pseudo-code snippets to give intuition into how the library calls work. In every function's arguments, anything before the `;` is a public input, anything after it is secret to the player performing the operation.* ### Setup Each player should generate a `PlayerSecretKey` and `PlayerPublicKey`. To register to play the game, a player publishes her `PlayerPublicKey` along with a zero knowledge proof that she knows the corresponding `PlayerSecretKey`. When every player has registered and every proof is verified, we can publicly compute an `AggregatePublicKey`. This `AggregatePublicKey` is created in such a way that *nobody knows the corresponding secret key*. Anything encrypted using the `AggregatePublicKey` will require the collaboration of *all* players to decrypt. ![](https://i.imgur.com/AOgONdG.png) ### Operations on a card **Mask** A mask operation takes a public `Card`, some secret `Randomness` and the `AggregatePublicKey`, and outputs a `MaskedCard`. For those who are curious about the cryptography, this is essentially an $n$-out-of-$n$ threshold encryption. ``` mask(Card, AggregatePublicKey; Randomness) -> MaskedCard ``` > *A helpful analogy - part 1/4.* > > You can think of the `mask` operation as taking a single card and placing it in an opaque box which we call a `MaskedCard`. The box is equipped with a set of padlocks - one padlock per player. Opening the box requires that each player removes their padlock. > > When every card has been placed into its own box, the boxes can be stacked into a deck and passed around. > > There is one problem however: the boxes have labels. Whoever put the cards in the boxes will know the mapping from box labels to card values. We fix this with the `remask` operation. > ![Mask Diagram](https://i.imgur.com/yDSHA1U.png) Important properties: - ✅ A `MaskedCard` *hides* the value of the underlying card. - ✅ A `MaskedCard` can only be unmasked by a coalition of *all* players. - ⚠️ The player who performed the `mask` operation knows the mapping from `MaskedCard` to card value. - ⚠️ Any player who learns the random value $\rho$ that was used can find what card was masked by brute force: simply mask all the cards with $\rho$, see which one yields the same `MaskedCard`. Unfortunately masking is not enough to guarantee that no player can know a `MaskedCard`'s value. To fix this, we get all other players to "remask" the `MaskedCard`. **Remask** The `remask` operation expects a `MaskedCard`, the `AggregatePublicKey`, some secret randomness and outputs a new `MaskedCard` for the same underlying card value. ``` remask(MaskedCard, AggregatePublicKey; Randomness) -> MaskedCard ``` > *A helpful analogy - part 2/4.* > > The `remask` operation can be understood as secretly replacing the label on a box with a new label. Whoever performs the `remask` operation knows the mapping from new labels to old labels. > ![](https://i.imgur.com/Do4kXwG.png) Can we recover the value of the card after a `remask` operation? Yes! By collaborating with all the players that have masked and remasked the card before us. To guarantee that no player or group of players can cheat, we need to ensure that *all players* apply the `remask` operation. **Reveal and Unmask** After a card has been masked and remasked by all players, we finally find ourselves in situation where no sub-group of players can infer the value of a `MaskedCard`. How do we recover the card's value when it is time to show it? As mentioned previously, this is a collaborative process. First each player must produce a `RevealToken` for the target `MaskedCard` using the `reveal` function. ``` reveal(MaskedCard; PlayerSecretKey) -> RevealToken ``` These tokens are unique to each player and to each card, and can only be derived by knowing the right secret key. Importantly a `RevealToken` leaks no information about the secret key that was used to produce it. When every player has released their `RevealToken` for a target `MaskedCard`, anyone can collect them and apply `unmask`. This returns a publicly readable `Card`. ``` unmask(MaskedCard, Vec<RevealToken>;) -> Card ``` > *A helpful analogy - part 3/4.* > > When a player provides a `RevealToken` for a `MaskedCard`, they are effectively removing their lock from that specific box. Once all players have removed their padlocks, we can use the `unmask` function to open the box and recover the card that had been hidden. > ![](https://i.imgur.com/yJbWsUi.png) To make the game trustless we need to be able to convince all the players that these operations are performed correctly without revealing their secret inputs. Our library provides functions to **prove and verify each of these operations in zero knowledge**. ### Operations on a deck **Shuffle and remask** ``` shuffle_and_remask( Vec<MaskedCard>, MasterPublicKey; Vec<Randomess>, Permutation )-> Vec<MaskedCard> ``` `shuffle_and_remask` does exactly what it says: take a deck of `MaskedCard`, change the order and apply `remask` to each of them. Similarly to the `remask` operation, all players must apply `shuffle_and_remask` to guarantee that no sub-group can collaborate to cheat. > *A helpful analogy - part 4/4.* > > When shuffling a deck of physical cards, it is enough to turn them face down and change their order. In our case, recall that a `MaskedCard` is like a locked labeled box. If we only change their order, players will still be able to read the labels. To get the same effect as shuffling a deck of cards, we need to change the order of the boxes and apply new labels. > ![](https://i.imgur.com/mUCrPVV.png) **Split** *COMING SOON. This is just a special case of a shuffle.* ## A Round of Texas Hold'Em We will now see how these function calls can be used to play a round of Texas Hold'Em. ### Game setup | | Texas Hold'Em | Mental Texas Hold'Em | | -------- | -------- | -------- | |1.|Gather a group of players around a table| Players each run the key generation algorithm, publish their public key and prove in zk that they own the corresponding secret key. A master public key is computed. | | 2. | Bring a deck of 52 standard cards. | Publicly encode each card value to a `Card`. `mask` all 52 `Card` with a public randomness value and publish all proofs. | ### An Example Round | | Texas Hold'Em | Mental Texas Hold'Em | | -------- | -------- | -------- | | 3. | Dealer shuffles the deck | Players take turns running `shuffle_and_remask` and publish the proof for their shuffle. | |4.| Give out the cards: top card goes to the first player, second card goes to second, etc...|Publish reveal tokens (with proofs) to allow players to see their cards: all players but the first publish a `RevealToken` for the first card, all players but the second publish a `RevealToken` for the second card, etc...| |5.|Betting using physical chips|Betting using on-chain currency.| |6.|Discard and reveal cards following the game rules|Following the order of the deck, ignore discarded cards. To open a card, publish reveal tokens (with proofs) and `unmask` cards following the game rules.| |7.|Repeat 5 and 6 as required by the rules|Repeat 5 and 6 as required by the rules| |8.|Final betting. Open hands and declare a winner|Final betting. Publish reveal tokens (with proofs), `unmask` all the cards, compare their values and declare a winner.| Throughout this description, we have omitted the verification steps. This is because these verifications might be performed by different parties (players, off-chain server, on-chain smart contract) depending on the trust model that the game developer has chosen. ## Technical Notes The library is: - written in Rust using the [`arkworks`](https://github.com/arkworks-rs) ecosystem. - based on elliptic curve cryptography. Game designers can choose the curve with which they want to work. - compatible with Cairo and StarkNet. We have implemented the necessary finite field arithmetic to allow our types (`Card`, `MaskedCard`, `AggregatePublicKey`, proofs, etc...) to be represented using Cairo's native `felt` type. - mostly modular. We have instantiated the card protocol from [2] with a specific encryption scheme, commitment scheme and a zk argument for a correct shuffle. Each of these can be replaced (e.g. replace the shuffle argument by a general purpose zk-SNARK). ## Contact Details Game developers please get in touch here: [nico@geometry.xyz](mailto:nico@geometry.xyz). We can help you integrate the library to your game, implement features on request and improve the library to simplify your experience. ## References 1. A. Shamir, R. Rivest, and L. Adleman, "Mental Poker", Technical Memo LCS/TM-125, Massachusetts Institute of Technology, April 1979. https://apps.dtic.mil/dtic/tr/fulltext/u2/a066331.pdf 2. Barnett, Adam, and Nigel P. Smart. "Mental poker revisited." In *IMA International Conference on Cryptography and Coding*, pp. 370-383. Springer, Berlin, Heidelberg, 2003. 3. Bayer, Stephanie, and Jens Groth. "Efficient zero-knowledge argument for correctness of a shuffle." In *Annual International Conference on the Theory and Applications of Cryptographic Techniques*, pp. 263-280. Springer, Berlin, Heidelberg, 2012