Skip to main content

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

Build docs developers (and LLMs) love