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.

Quick start

This guide will walk you through creating and running your first RISC Zero zkVM application. You’ll build a simple program that proves knowledge of two numbers that multiply to a specific product, without revealing the factors themselves.

Prerequisites

Before you begin, make sure you have installed the RISC Zero toolchain.

Create a new project

Use cargo risczero to create a new project from the default template:
cargo risczero new my_project
This creates a new directory with a complete zkVM project structure.
You can customize the project creation with additional flags:
  • --no-std: Create a project with no_std in the guest
  • --no-git: Disable git initialization
  • --template <URL>: Use a custom template from GitHub

Project structure

zkVM applications are organized into a host program and a guest program:
my_project/
├── src/
│   └── main.rs          # Host program
└── methods/
    ├── guest/
    │   └── src/
    │       └── main.rs  # Guest program
    └── build.rs
  • Host: Executes the guest program and generates the proof
  • Guest: The code that runs inside the zkVM and gets proven

Understanding the hello-world example

Let’s examine the default hello-world example that demonstrates proving knowledge of factors.

Guest code

The guest program runs inside the zkVM. Here’s the complete guest code:
methods/guest/src/main.rs
#![no_main]
#![no_std]

use risc0_zkvm::guest::env;

risc0_zkvm::guest::entry!(main);

fn main() {
    // Load the first number from the host
    let a: u64 = env::read();
    // Load the second number from the host
    let b: u64 = env::read();
    // Verify that neither of them are 1 (i.e. nontrivial factors)
    if a == 1 || b == 1 {
        panic!("Trivial factors")
    }
    // Compute the product while being careful with integer overflow
    let product = a.checked_mul(b).expect("Integer overflow");
    env::commit(&product);
}
Key points:
  • env::read() reads input from the host
  • env::commit() writes the output to the journal
  • The guest code validates that the factors are non-trivial

Host code

The host program executes the guest and generates the proof:
src/lib.rs
use hello_world_methods::MULTIPLY_ELF;
use risc0_zkvm::{ExecutorEnv, Receipt, default_prover};

pub fn multiply(a: u64, b: u64) -> (Receipt, u64) {
    let env = ExecutorEnv::builder()
        // Send a & b to the guest
        .write(&a)
        .unwrap()
        .write(&b)
        .unwrap()
        .build()
        .unwrap();

    // Obtain the default prover.
    let prover = default_prover();

    // Produce a receipt by proving the specified ELF binary.
    let receipt = prover.prove(env, MULTIPLY_ELF).unwrap().receipt;

    // Extract journal of receipt (i.e. output c, where c = a * b)
    let c: u64 = receipt.journal.decode().expect(
        "Journal output should deserialize into the same types (& order) that it was written",
    );

    // Report the product
    println!("I know the factors of {c}, and I can prove it!");

    (receipt, c)
}
The main function uses this library code:
src/main.rs
use hello_world::multiply;
use hello_world_methods::MULTIPLY_ID;

fn main() {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    // Pick two numbers
    let (receipt, _) = multiply(17, 23);

    // Here is where one would send 'receipt' over the network...

    // Verify receipt, panic if it's wrong
    receipt.verify(MULTIPLY_ID).expect(
        "Code you have proven should successfully verify; did you specify the correct image ID?",
    );
}

Run the example

Navigate to your project directory and run:
cd my_project
cargo run --release
The first run may take several minutes as it compiles the guest code and generates the proof. Subsequent runs will be faster.
You should see output similar to:
I know the factors of 391, and I can prove it!
Congratulations! You just constructed a zero-knowledge proof that you know the factors of 391 (17 × 23).

What just happened?

1

Host sends inputs

The host program sent the numbers 17 and 23 to the guest program.
2

Guest computes product

The guest program computed 17 × 23 = 391 and committed the result to the journal.
3

Proof generation

The zkVM generated a cryptographic proof (receipt) of correct execution.
4

Verification

The host verified the receipt using the method’s image ID, confirming the computation was performed correctly.

Key concepts demonstrated

Receipt

The proof of correct execution. It contains:
  • Journal: The public output (391 in this example)
  • Seal: Cryptographic proof data

Image ID

A cryptographic hash of the guest program (MULTIPLY_ID). Used during verification to ensure the correct program was executed.

Zero-knowledge

The verifier learns that 391 is composite and that the prover knows the factors, but does not learn the actual factors (17 and 23).

Dependencies

Your Cargo.toml should include:
Cargo.toml
[dependencies]
risc0-zkvm = { version = "2.1" }
For the guest program:
methods/guest/Cargo.toml
[dependencies]
risc0-zkvm = { version = "2.1", default-features = false }
Guest programs must use default-features = false to avoid including std library features that aren’t available in the zkVM environment.

Feature flags

Common feature flags for risc0-zkvm:
  • prove: Enables the prover (required for generating proofs)
  • cuda: Enables CUDA GPU acceleration
  • client: Enables the client API

Building deterministically

To ensure your guest code produces the same ImageID across different build environments:
cargo risczero build --manifest-path methods/guest/Cargo.toml
This uses Docker to build your guest code in a reproducible environment.

Next steps

Examples

Explore more example projects

API Reference

Browse the complete API documentation

Developer docs

Learn advanced concepts and best practices

Tutorial

Follow the step-by-step tutorial