Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt

Use this file to discover all available pages before exploring further.

Prove that a chess position has a checkmate without revealing what the checkmate move is, demonstrating zero-knowledge proofs for game logic.

What You’ll Learn

  • Using external Rust crates in the zkVM
  • Implementing game logic with zero-knowledge
  • Working with chess notation (FEN and SAN)
  • Privacy-preserving competitive gaming

Overview

This example uses the shakmaty crate to:
  • Parse chess positions in FEN format
  • Validate moves in SAN format
  • Check for checkmate conditions
  • Prove a mate-in-one exists without revealing it
The prover knows the winning move, but only the initial board position is revealed in the journal.

How It Works

1
Guest Program
2
The guest receives a board and move, verifies the move leads to checkmate:
3
use chess_core::Inputs;
use risc0_zkvm::guest::env;
use shakmaty::{
    CastlingMode, Chess, FromSetup, Move, Position, Setup,
    fen::Fen, san::San,
};

fn main() {
    let inputs: Inputs = env::read();
    let mv: String = inputs.mv;
    let initial_state: String = inputs.board;
    
    // Commit only the initial board state
    env::commit(&initial_state);

    // Parse the position
    let setup = Setup::from(
        Fen::from_ascii(initial_state.as_bytes()).unwrap()
    );
    let pos = Chess::from_setup(setup, CastlingMode::Standard).unwrap();

    // Parse and apply the move
    let mv: Move = mv.parse::<San>()
        .unwrap()
        .to_move(&pos)
        .unwrap();
    let pos = pos.play(&mv).unwrap();
    
    // Verify it's checkmate
    assert!(pos.is_checkmate());
}
4
Key Points:
5
  • The move is read but never committed
  • Only the initial board state appears in the journal
  • The program panics if the move doesn’t result in checkmate
  • 6
    Host Program
    7
    The host provides the secret move and generates the proof:
    8
    use chess_core::Inputs;
    use chess_methods::{CHECKMATE_ELF, CHECKMATE_ID};
    use clap::Parser;
    use risc0_zkvm::{ExecutorEnv, default_prover};
    
    #[derive(Parser)]
    struct Cli {
        #[arg(id = "MOVE", default_value = "Qxf7")]
        mv: String,
    
        #[arg(default_value = "r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4")]
        board: String,
    }
    
    fn main() {
        let args = Cli::parse();
    
        let inputs = Inputs {
            board: args.board,
            mv: args.mv,
        };
    
        // Generate proof
        let env = ExecutorEnv::builder()
            .write(&inputs)
            .unwrap()
            .build()
            .unwrap();
    
        let receipt = default_prover()
            .prove(env, CHECKMATE_ELF)
            .unwrap()
            .receipt;
    
        // Verify receipt
        receipt.verify(CHECKMATE_ID).unwrap();
        
        let committed_state: String = receipt.journal.decode().unwrap();
        assert_eq!(inputs.board, committed_state);
    
        println!("There is a checkmate in this position:");
        // Display the board...
    }
    

    Running the Example

    1
    Default Example
    2
    Run with the default position (Scholar’s Mate):
    3
    cargo run --release
    
    4
    Output:
    5
    There is a checkmate for White in this position:
     r . b q k b . r
     p p p p . p p p
     . . n . . n . .
     . . . . p . . Q
     . . B . P . . .
     . . . . . . . .
     P P P P . P P P
     R N B . K . N R
    
    6
    Custom Position
    7
    Provide your own position and move:
    8
    cargo run --release -- "Qh5" "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2"
    
    9
    Try Different Mates
    10
    # Back rank mate
    cargo run --release -- "Rd8" "6k1/5ppp/8/8/8/8/5PPP/4R1K1 w - - 0 1"
    
    # Smothered mate
    cargo run --release -- "Nf7" "r1bqk2r/pppp1ppp/2n5/2b1p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1"
    

    What Gets Proven?

    The receipt proves:
    1. Checkmate Exists: There is a legal move that results in checkmate
    2. From This Position: The checkmate is from the committed board state
    3. Move is Secret: The winning move is never revealed

    Zero-Knowledge Property

    Anyone with the receipt can verify:
    • The board position has a mate-in-one
    • The prover knows the move
    But cannot learn:
    • What the checkmate move is
    • Any details about the move

    Chess Notation

    FEN (Forsyth-Edwards Notation)

    Describes a chess position:
    rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
    
    Components:
    1. Piece placement: Board from rank 8 to 1
    2. Active color: w (white) or b (black)
    3. Castling rights: KQkq (both sides can castle)
    4. En passant: Target square if available
    5. Halfmove clock: Moves since last capture/pawn move
    6. Fullmove number: Current move number

    SAN (Standard Algebraic Notation)

    Describes a move:
    Qxf7#  - Queen captures on f7, checkmate
    O-O    - Kingside castling
    Nf3    - Knight to f3
    e4     - Pawn to e4
    

    Use Cases

    Puzzle Contests

    Offer rewards for finding checkmates without revealing solutions:
    Contest Creator                     Solver
    ├─ Post position                  │
    ├─ Offer reward                   ├─ Find checkmate
    │                                  ├─ Generate proof
    ├─ Receive proof                  │
    ├─ Verify (no move revealed)      │
    └─ Pay reward                     └─ Receive payment
    
    Benefits:
    • First solver proven fairly
    • Solution stays secret for other solvers
    • Trustless reward distribution

    Tournament Integrity

    Prove game outcomes without revealing moves:
    Player A vs Player B
        ├─ Play game offline
        ├─ Generate proofs for key positions
        └─ Submit results with proofs
    
    Tournament Verifier
        ├─ Verify all proofs
        ├─ Confirm game validity
        └─ Award points
    

    Training Systems

    Verify students found tactics without revealing solutions:
    Teacher: "Find the mate in this position"
    Student: Generates proof
    Teacher: Verifies proof
        └─ Confirmed student found it
        └─ Solution still secret for other students
    

    Onchain Chess

    Integrate with blockchain games:
    function claimCheckmate(
        bytes memory board,
        bytes memory receipt
    ) external {
        // Verify the zkVM receipt
        risc0Verifier.verify(receipt, CHECKMATE_ID);
        
        // Extract board from journal
        string memory committedBoard = abi.decode(
            receipt.journal,
            (string)
        );
        
        // Award points/tokens
        rewards.mint(msg.sender, CHECKMATE_REWARD);
    }
    

    Using the Shakmaty Crate

    The shakmaty crate provides comprehensive chess logic:

    Position Parsing

    use shakmaty::{Chess, Setup, fen::Fen};
    
    let fen = Fen::from_ascii(b"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")?;
    let setup = Setup::from(fen);
    let pos = Chess::from_setup(setup, CastlingMode::Standard)?;
    

    Move Parsing

    use shakmaty::san::San;
    
    let san: San = "Nf3".parse()?;
    let mv = san.to_move(&pos)?;  // Validates move is legal
    

    Position Updates

    let new_pos = pos.play(&mv)?;  // Apply move
    

    Checking Game State

    if pos.is_checkmate() {
        println!("Checkmate!");
    }
    
    if pos.is_check() {
        println!("Check!");
    }
    
    if pos.is_stalemate() {
        println!("Stalemate!");
    }
    
    if pos.is_insufficient_material() {
        println!("Draw by insufficient material");
    }
    
    let legal_moves: Vec<Move> = pos.legal_moves().collect();
    println!("Legal moves: {}", legal_moves.len());
    

    Extending the Example

    Mate-in-N

    Extend to prove mate-in-N moves:
    fn main() {
        let inputs: MateInNInputs = env::read();
        let moves: Vec<String> = inputs.moves;  // Secret sequence
        let mut pos = parse_position(&inputs.board);
        
        // Apply all moves
        for mv_str in moves {
            let mv = mv_str.parse::<San>()?.to_move(&pos)?;
            pos = pos.play(&mv)?;
        }
        
        // Verify checkmate
        assert!(pos.is_checkmate());
        
        // Commit only initial position and move count
        env::commit(&(inputs.board, moves.len()));
    }
    

    Game Recording

    Prove entire game validity:
    fn main() {
        let moves: Vec<String> = env::read();
        let mut pos = Chess::default();  // Starting position
        
        // Play all moves
        for mv_str in &moves {
            let mv = mv_str.parse::<San>()?.to_move(&pos)?;
            pos = pos.play(&mv)?;
        }
        
        // Verify game ended properly
        assert!(pos.is_checkmate() || pos.is_stalemate());
        
        // Commit final position
        env::commit(&pos.board().to_string());
    }
    

    Opening Verification

    Prove game followed specific opening:
    fn main() {
        let moves: Vec<String> = env::read();
        let opening_moves = vec!["e4", "e5", "Nf3", "Nc6"];  // Italian Game
        
        // Verify opening moves match
        assert!(moves.starts_with(&opening_moves));
        
        // ... verify rest of game
    }
    

    Performance

    MetricValue
    Cycles (mate-in-1)~5M
    Proving time~5-10 seconds
    Receipt size~128 KB
    Memory usage~2 GB
    Performance scales with:
    • Move sequence length
    • Position complexity
    • Number of legal moves evaluated

    Rust Crates in zkVM

    This example demonstrates using pure-Rust crates: Works in zkVM:
    • Pure Rust implementation
    • No system calls
    • Supports no_std (with features)
    • Deterministic behavior
    shakmaty works because:
    • Pure Rust chess logic
    • No external dependencies on system libraries
    • Well-structured and efficient
    See Rust Resources for more on crate compatibility.

    Video Tutorial

    For a detailed walkthrough, see this excerpt from our workshop at ZK HACK III.

    Bonsai Integration

    Use as a Bonsai application for onchain chess:
    // Submit checkmate proof to Bonsai
    let image_id = CHECKMATE_ID;
    let input = borsh::to_vec(&inputs)?;
    
    let session = bonsai_sdk::alpha::SessionId::new(
        image_id,
        input,
    )?;
    
    // Wait for proof
    let receipt = session.receipt()?;
    
    // Submit to blockchain
    submit_to_chain(receipt).await?;
    
    See the blog post on Bonsai as a zk coprocessor.

    Next Steps