Mantanetwork

Mantanetwork zkShuffle encrypted decks and private card dealing

Last updated:

Mantanetwork's zkShuffle toolkit uses encrypted decks and zero-knowledge proofs to support private dealing in on-chain card games. A zero-knowledge proof lets a contract check a shuffle without learning its secret ordering. Circom circuits describe the cryptographic conditions, Solidity contracts manage the shared deck, and a TypeScript SDK connects the application. The game contract adds legal moves and winning rules. Private dealing depends on players' keys and decryption contributions; public reveals deliberately expose selected card values on-chain.

Bottom line: A valid shuffle rearranges the deck without replacing its cards, while game contracts determine the meaning of each reveal.

Circuits, contracts, and the game interface

zkShuffle's developer toolkit separates cryptographic checks from the rules that give cards meaning in a game. Mental poker describes protocols for playing card games over a communication system without a trusted dealer. A circuit encodes mathematical conditions that a proof must satisfy. Circom compiles these conditions into a form that the proving software understands. Generated Solidity verifier contracts check proofs, and the shuffle manager coordinates the encrypted deck. The TypeScript SDK prepares proofs and manages the game keys that those operations need.

Game developers implement IBaseGame alongside the manager interface. Their contracts specify the card configuration, control the game's progression, and decide how revealed cards affect play. A verified public opening links a revealed value to the encrypted card; the game rules decide whether its use is legal.

Secret permutations and a verifiable deck

An encrypted shuffle changes card order while preserving the contents of the incoming deck. A permutation is a rearrangement that uses each input position exactly once. zkShuffle's published design combines that rearrangement with ElGamal encryption, which lets participants transform encrypted cards without exposing their values. Players contribute their own shuffles, so later participants work on the encrypted output that earlier participants produced.

Mantanetwork - Secret permutations and a verifiable deck - diagram

Open full-size image

A zkShuffle shuffle proof verifies the relationship between the incoming encrypted deck and the outgoing encrypted deck. The circuit checks both the permutation and the encryption operation. It keeps the permutation and encryption randomness private. A valid transformation doesn't establish that its randomness was honestly chosen; the scheme's fairness depends on at least one participant contributing an unpredictable shuffle.

Decryption contributions and private hands

Private dealing leaves the intended recipient's encryption contribution in place until that recipient decrypts locally. The other participants provide their decryption contributions for the selected cards. Each public decryption contribution carries a proof that checks the operation without disclosing that contributor's secret key. The cryptographic scheme permits other contributors to decrypt in different orders, although the application can enforce its own turn order.

Visibility at the final decryption

Opening a card locally

In the documented SDK, openOffchain decrypts selected cards on the player's machine without sending the decrypted values to the blockchain. Its privacy relies on the preceding dealing state and the recipient's secret key. Other required contributors must already have completed their part of the card's decryption.

Local decryption keeps the displayed value off-chain. The application must also avoid transmitting that value through logs, analytics, or other frontend requests.

Revealing a card publicly

The zkShuffle SDK's open method sends card-opening data to the blockchain. That operation supports public revelation of selected cards, with a proof linking the revealed value to the encrypted card. Choosing it for a private hand exposes information that the local opening path keeps on the player's machine.


Game phases and their completion conditions

The shuffle manager is a state machine that tracks which kind of action the game currently accepts. Its register and shuffle phase controls initiate work that players then perform. Setting a phase doesn't complete the cryptographic work. The next callback runs after the manager's required contributions arrive, so an application's displayed phase and the deck's completed state can differ.

Visual summary: Game phases and their completion conditions (Mantanetwork)

Open full-size image

Registration waits for all required players, and shuffling waits for their shuffle contributions. During private dealing, the manager waits for every player except the selected recipient. The game contract specifies the next callback and the authorized player for an opening phase. Missing contributions can hold up the sequence. The application's rules must define the consequence of a participant withholding a required contribution.


Keys, setup parameters, and circuit reuse

Player encryption keys protect the secret information that the deck protocol hides from observers. A proof's secret inputs form a witness: the values that satisfy the circuit's mathematical constraints.

zkShuffle's published construction uses Groth16, a proof system that requires circuit-specific trusted setup parameters. The toolkit's build produces WebAssembly code for witness calculation, proving-key files, and Solidity verifiers. The proving artifacts and deployed verifier must correspond to the same circuit and setup. Changing a circuit requires rebuilding the matching artifacts. The circuit must also encode every mathematical condition that verification promises to establish. Protecting players' encryption keys remains essential even when proofs verify, because zero-knowledge verification doesn't protect a secret that an application exposes.

The v2 circuit review dated February 1, 2024 identified a reuse condition in the ecDecompress helper. Its surrounding code constrained a sign selector to a Boolean value, and the review found no impact in that implementation. Reusing the helper without preserving that constraint could allow invalid proofs. A Boolean selector must take only 0 or 1; merely assigning a value doesn't enforce that restriction.

Illustration: Mantanetwork - Keys, setup parameters, and circuit reuse

Open full-size image


Proof computation and transaction costs

Proof generation consumes client computing resources, while on-chain verification and state changes consume gas. Network finality concerns the accepted transaction state; preparing a card proof involves separate computation. The total delay also includes any time that participants take to contribute their required actions.

In the published matrix-based construction, the permutation matrix's dimensions follow the selected deck size. Larger decks therefore require more permutation entries. Transaction cost also depends on submitted data, contract work, and the deployment network's fee rules. There isn't a single fixed cost for every game or reveal.

SDK operations for shuffling, dealing, and revealing

The SDK changes the encrypted deck, opens a card, or reads existing state.

Overview: SDK operations for shuffling, dealing, and revealing
SDK method Purpose Blockchain effect Limit for private play
shuffle Shuffle the encrypted deck Submits the shuffle and proof Proof validity doesn't establish honest randomness
draw Perform the dealing action Submits a dealing proof Doesn't replace local card opening
openOffchain Decrypt selected cards locally Doesn't submit decrypted values Needs the recipient's key and completed dealing contributions
open Reveal selected cards publicly Submits opening data and proof Exposes the selected cards
queryCards Read card values from contract state Reads existing on-chain data Doesn't perform the recipient's secret decryption

Private card handling within a complete game

Mantanetwork introduced zkShuffle within Universal Circuits, its library of reusable ZK functionality for Solidity applications. Manta Pacific supplies an Ethereum-compatible contract environment. The card toolkit adds application-level handling of hidden information, while the game contract determines what that information means for play. Keeping those roles separate explains why encrypted cards can coexist with public game state. The chain exposes the data that an application publishes.

The zkHiLo tutorial uses two players and a 52-card configuration, with the larger drawn card value deciding the winner. Its Hilo contract controls the game sequence through callbacks from the manager.

Mantanetwork questions, answered

Can Mantanetwork zkShuffle use a deck other than 52 cards?

Yes, the contract interface includes a 30-card configuration alongside the tutorial's 52-card configuration. The game contract selects a supported configuration through cardConfig. Deck size must match the circuit and verifier that implement it. Selecting a preset doesn't establish support for every arbitrary deck length or card encoding.

What does -1 mean when zkShuffle opens a card?

The SDK uses -1 to report an unsuccessful opening for the requested card. It isn't a card rank or a successful reveal. The return value alone doesn't specify the underlying cause, and an application shouldn't use it to score a hand.

What does a zkShuffle player ID identify within a game?

A player ID identifies a participant within one shuffle game. The game ID identifies the game itself. joinGame returns the joining participant's player ID, and getPlayerId retrieves the relevant ID for an existing game. Contract parameters that identify the recipient expect the player index for that game, so a wallet address or a game ID can't serve as that index.

Does zkShuffle use the same key for wallet signing and card encryption?

The registration interface treats the signing address and card-encryption public key as separate inputs. The signing address identifies the participant's transaction signer. The encryption public key has coordinates that the SDK can generate for the deck protocol. Having a wallet address doesn't provide the secret that completes private card decryption. An integration needs the game key associated with the registered encryption public key.

Where can an application see which players have contributed to a card's decryption?

The manager's getDecryptRecord query returns the participation record for a selected card. The contract represents that record as a bitmap, where individual bits identify players who have already contributed. It describes progress on that card, not the player's entire hand or the whole game's completion. The surrounding dealing phase determines which contributions the recipient needs.

How can a zkShuffle game restrict access to its callbacks?

A game contract can restrict a callback to calls from its configured shuffle manager. The Hilo implementation does this with an onlyShuffleManager modifier that compares the caller with the manager address. The developer must apply that restriction to the appropriate game functions. A valid card proof and permission to advance the game are separate conditions; the proof alone doesn't supply callback authorization.

Are zkShuffle card positions the same as decrypted card values?

A card position identifies a location in the shuffled deck, while a decrypted value identifies the card. For a publicly opened card, queryCardValue retrieves the value associated with the selected deck index. Shuffling changes which card occupies a position, so the index alone doesn't reveal the card's rank or identity. Scoring logic must distinguish the actual card value from its shuffled position.