This post extends the tic-tac-toe Petri net model with zero-knowledge proofs. Using gnark, we can cryptographically verify that a move is valid without revealing the player's strategy or the complete game state.
Try the interactive demo at pilot.pflow.xyz/zk-tic-tac-toe.
In the basic tic-tac-toe model, all state is visible. Both players see:
But what if we want private strategy? Or verifiable computation where a third party confirms moves are legal without seeing the game? Zero-knowledge proofs make this possible.
A zero-knowledge proof for tic-tac-toe demonstrates:
All without revealing:
The gnark circuit encodes the Petri net's transition rules as arithmetic constraints:
// Simplified circuit structure
type MoveCircuit struct {
// Public inputs
PrevStateHash frontend.Variable `gnark:",public"`
NewStateHash frontend.Variable `gnark:",public"`
PlayerTurn frontend.Variable `gnark:",public"`
// Private witness
Position frontend.Variable // Which cell (0-8)
PrevMarking [10]frontend.Variable // Previous token state
NewMarking [10]frontend.Variable // New token state
}
The circuit enforces:
1. Position ∈ {0, 1, 2, 3, 4, 5, 6, 7, 8}
2. PrevMarking[Position] == 1 (cell was empty)
3. NewMarking[Position] == 0 (cell now claimed)
4. Turn token moved correctly
5. Hash(PrevMarking) == PrevStateHash
6. Hash(NewMarking) == NewStateHash
Each Petri net rule becomes an R1CS constraint:
| Petri Net Rule | Circuit Constraint |
|---|---|
| Cell available | prev_marking[pos] * 1 == 1 |
| Cell claimed | new_marking[pos] * 1 == 0 |
| Turn consumed | prev_turn[player] == 1 |
| Turn produced | new_turn[next_player] == 1 |
| State unchanged elsewhere | ∀i≠pos: prev[i] == new[i] |
The total circuit has ~100 constraints for a single move verification.
When a player makes a move:
1. Compute new marking (fire transition locally)
2. Hash the new state
3. Generate ZK proof with gnark
4. Send: (prev_hash, new_hash, player_turn, proof)
The verifier checks the proof without seeing the actual move:
1. Verify proof against public inputs
2. If valid: accept new_hash as canonical state
3. If invalid: reject (cheating detected)
Petri nets and ZK circuits share a key property: local verification. A transition fires based only on its input places, not global state. This maps directly to R1CS constraints.
Benefits of the combination:
| Property | Petri Net | ZK Circuit |
|---|---|---|
| Composable | Subnets combine | Circuits compose |
| Local | Transitions check inputs | Constraints check witnesses |
| Deterministic | Same inputs → same outputs | Same witness → same proof |
| Verifiable | Marking evolution | Proof verification |
A complete ZK tic-tac-toe game works as:
Initial: state_hash = hash(empty_board)
Round n:
1. Current player generates move locally
2. Computes new_state_hash
3. Generates ZK proof: (old_hash, new_hash, proof)
4. Opponent verifies proof
5. If valid: state_hash = new_hash
6. Continue until win/draw detected
Win detection can be a separate ZK proof that demonstrates three-in-a-row without revealing the full board.
Gnark proof generation for a single move:
| Operation | Time | Notes |
|---|---|---|
| Circuit compilation | ~500ms | One-time setup |
| Witness generation | ~5ms | Per move |
| Proof generation | ~100ms | Per move |
| Verification | ~2ms | Per move |
The bottleneck is proof generation, but 100ms is acceptable for turn-based games.
The Petri net + ZK pattern applies to:
Any system modeled as a Petri net can gain privacy through ZK circuits.
The ZK version adds cryptographic machinery to the base model:
Standard Petri Net:
Places: P00-P22 (cells), Next (turn), _X*, _O* (history)
Transitions: X00-X22, O00-O22
ZK Extension:
Circuit: MoveCircuit (gnark R1CS)
Public: state_hash, player_turn
Private: position, prev_marking, new_marking
Proof System: Groth16 (gnark default)
| Concept | ZK Tic-Tac-Toe Example |
|---|---|
| Zero-knowledge | Prove validity without revealing move |
| Petri net → circuit | Transition rules become R1CS constraints |
| State commitment | Hash of marking hides actual state |
| Local verification | Only check changed places |
| Composability | Win detection as separate proof |
Zero-knowledge proofs extend Petri nets from verifiable to privately verifiable. The same structural properties that make Petri nets analyzable—locality, composability, determinism—make them natural targets for ZK circuits.
The combination enables a new class of applications: systems where we can prove correct behavior without revealing what that behavior was. The Petri net provides the model; the ZK circuit provides the privacy.
For the base tic-tac-toe model: Tic-Tac-Toe Model
For gnark documentation: gnark.io