Skip to main content
The #[jolt::provable] macro is the primary attribute macro for creating zero-knowledge provable functions in Jolt. It generates all necessary infrastructure for compiling, proving, and verifying function execution.

Overview

When applied to a function, #[jolt::provable] generates:
  • A main function for the guest program (RISC-V target)
  • Memory configuration functions
  • Build, prove, and verify functions for the host
  • Preprocessing and compilation utilities
  • Program analysis and tracing tools

Basic Usage

#[jolt::provable]
fn add(x: u32, y: u32) -> u32 {
    x + y
}
This generates functions like prove_add, build_verifier_add, compile_add, etc. that can be called from the host side.

Parameters

All parameters are optional and specified using the attribute syntax:
max_input_size
u64
default:"4096"
Maximum size in bytes for serialized input parameters.
max_output_size
u64
default:"4096"
Maximum size in bytes for serialized return value.
max_untrusted_advice_size
u64
default:"0"
Maximum size in bytes for untrusted advice parameters.
max_trusted_advice_size
u64
default:"0"
Maximum size in bytes for trusted advice parameters (requires commitment).
stack_size
u64
default:"4096"
Stack size in bytes for the guest program.
heap_size
u64
default:"4096"
Heap size in bytes for the guest program.
max_trace_length
u64
default:"1048576"
Maximum number of CPU cycles (trace length) for execution.
backtrace
bool
default:"false"
Enable backtrace support for debugging guest panics.
profile
bool
default:"false"
Enable profiling instrumentation in the guest program.
guest_only
bool
default:"false"
Generate only guest code, skip host-side utilities (for library functions).
wasm
bool
default:"false"
Generate WebAssembly verifier bindings.

Examples

Simple Function

#[jolt::provable]
fn muldiv(a: u32, b: u32, c: u32) -> u32 {
    a * b / c
}

Custom Memory Configuration

#[jolt::provable(heap_size = 32768, max_trace_length = 65536)]
fn fib(n: u32) -> u128 {
    let mut a: u128 = 0;
    let mut b: u128 = 1;
    let mut sum: u128;
    
    for _ in 1..n {
        sum = a + b;
        a = b;
        b = sum;
    }
    b
}

With Advice Parameters

#[jolt::provable(
    max_untrusted_advice_size = 8192,
    max_trusted_advice_size = 4096
)]
fn verify_with_advice(
    public_input: u32,
    witness: jolt::UntrustedAdvice<Vec<u8>>,
    commitment: jolt::TrustedAdvice<[u8; 32]>,
) -> bool {
    // Function body
    true
}

Generated Functions

For a function named foo, the macro generates (on the host side):
  • compile_foo(target_dir: &str) -> jolt::host::Program - Compiles the guest program
  • preprocess_shared_foo(program: &mut Program) -> JoltSharedPreprocessing - Generates shared preprocessing
  • preprocess_prover_foo(shared: JoltSharedPreprocessing) -> JoltProverPreprocessing - Generates prover preprocessing
  • preprocess_verifier_foo(shared: JoltSharedPreprocessing, setup: VerifierSetup) -> JoltVerifierPreprocessing - Generates verifier preprocessing
  • build_prover_foo(program: Program, preprocessing: JoltProverPreprocessing) -> impl Fn(...) - Creates a prover closure
  • build_verifier_foo(preprocessing: JoltVerifierPreprocessing) -> impl Fn(...) - Creates a verifier closure
  • prove_foo(program: Program, preprocessing: JoltProverPreprocessing, ...) -> (ReturnType, Proof, JoltDevice) - Generates a proof
  • analyze_foo(...) -> ProgramSummary - Analyzes program without proving
  • trace_foo_to_file(target_dir: &str, ...) - Exports execution trace
  • memory_config_foo() -> MemoryConfig - Returns the memory configuration

Parameter Types

Function parameters can be:
  • Public inputs: Regular parameters (e.g., x: u32)
  • Untrusted advice: Wrapped in jolt::UntrustedAdvice<T> - provided by prover, not committed
  • Trusted advice: Wrapped in jolt::TrustedAdvice<T> - committed via Pedersen, verified by constraints
All types must implement serde::Serialize and serde::Deserialize.

Advice Parameters

Trusted and untrusted advice parameters allow the prover to provide additional data:
#[jolt::provable(max_untrusted_advice_size = 1024)]
fn verify_factors(n: u64, factors: jolt::UntrustedAdvice<(u64, u64)>) -> bool {
    let (a, b) = *factors;
    jolt::check_advice_eq!(a * b, n);
    a > 1 && b > 1
}
Untrusted advice is not committed and costs minimal overhead. Use for witness data that will be fully checked. Trusted advice is Pedersen-committed and batched into the proof opening. Use when the advice must be bound to the proof but fully checking it would be too expensive.

Multiple Functions

You can mark multiple functions with #[jolt::provable] in the same crate:
#[jolt::provable(heap_size = 65536, max_trace_length = 65536)]
fn add(x: u32, y: u32) -> u32 {
    x + y
}

#[jolt::provable(heap_size = 65536, max_trace_length = 65536)]
fn mul(x: u32, y: u32) -> u32 {
    x * y
}
Use the JOLT_FUNC_NAME environment variable to select which function to compile:
JOLT_FUNC_NAME=add cargo build --target riscv64gc-unknown-linux-gnu

Build docs developers (and LLMs) love