Skip to main content

Overview

Preprocessing functions generate reusable data structures required for efficient proving and verification. The #[jolt::provable] macro generates several preprocessing functions that must be called before proving or verifying.

Preprocessing Pipeline

The preprocessing pipeline has three stages:
  1. Shared preprocessing - Common data used by both prover and verifier
  2. Prover preprocessing - Prover-specific data derived from shared preprocessing
  3. Verifier preprocessing - Verifier-specific data derived from shared preprocessing
Program → Shared Preprocessing → Prover Preprocessing
                                → Verifier Preprocessing

Function Signatures

preprocess_shared

pub fn preprocess_shared_<function_name>(
    program: &mut jolt::host::Program
) -> jolt::JoltSharedPreprocessing
Generates shared preprocessing data from the compiled program. Parameters:
  • program - Mutable reference to the compiled guest program
Returns:
  • Shared preprocessing data containing:
    • Decoded bytecode
    • Memory layout
    • Memory initialization
    • Maximum trace length configuration

preprocess_prover

pub fn preprocess_prover_<function_name>(
    shared_preprocessing: jolt::JoltSharedPreprocessing
) -> jolt::JoltProverPreprocessing<jolt::F, jolt::PCS>
Generates prover-specific preprocessing from shared data. Parameters:
  • shared_preprocessing - The shared preprocessing output
Returns:
  • Prover preprocessing including commitment scheme generators and prover-specific state

preprocess_verifier

pub fn preprocess_verifier_<function_name>(
    shared_preprocessing: jolt::JoltSharedPreprocessing,
    generators: <jolt::PCS as jolt::CommitmentScheme>::VerifierSetup,
) -> jolt::JoltVerifierPreprocessing<jolt::F, jolt::PCS>
Generates verifier-specific preprocessing from shared data. Parameters:
  • shared_preprocessing - The shared preprocessing output
  • generators - Verifier setup for the commitment scheme (obtained from prover preprocessing)
Returns:
  • Verifier preprocessing containing all data needed for verification

verifier_preprocessing_from_prover

pub fn verifier_preprocessing_from_prover_<function_name>(
    prover_preprocessing: &jolt::JoltProverPreprocessing<jolt::F, jolt::PCS>
) -> jolt::JoltVerifierPreprocessing<jolt::F, jolt::PCS>
Convenience function to derive verifier preprocessing from prover preprocessing. Parameters:
  • prover_preprocessing - Reference to prover preprocessing
Returns:
  • Verifier preprocessing

Usage Examples

Standard Preprocessing Flow

let target_dir = "/tmp/jolt-guest-targets";

// Step 1: Compile the guest program
let mut program = guest::compile_fib(target_dir);

// Step 2: Generate shared preprocessing
let shared_preprocessing = guest::preprocess_shared_fib(&mut program);

// Step 3: Generate prover preprocessing
let prover_preprocessing = guest::preprocess_prover_fib(shared_preprocessing.clone());

// Step 4: Generate verifier preprocessing
let verifier_setup = prover_preprocessing.generators.to_verifier_setup();
let verifier_preprocessing = guest::preprocess_verifier_fib(
    shared_preprocessing,
    verifier_setup,
);

// Now ready to prove and verify
let prove_fib = guest::build_prover_fib(program, prover_preprocessing);
let verify_fib = guest::build_verifier_fib(verifier_preprocessing);

Alternative: Deriving Verifier from Prover

let mut program = guest::compile_fib(target_dir);
let shared_preprocessing = guest::preprocess_shared_fib(&mut program);
let prover_preprocessing = guest::preprocess_prover_fib(shared_preprocessing);

// Derive verifier preprocessing from prover
let verifier_preprocessing = guest::verifier_preprocessing_from_prover_fib(
    &prover_preprocessing
);

Serializing Preprocessing

Preprocessing data can be serialized and saved to disk for reuse:
use jolt_sdk::serialize_and_print_size;

let save_to_disk = true;
if save_to_disk {
    serialize_and_print_size(
        "Verifier Preprocessing",
        "/tmp/jolt_verifier_preprocessing.dat",
        &verifier_preprocessing,
    )
    .expect("Could not serialize preprocessing.");
}

Memory Configuration

The preprocessing uses memory configuration attributes from the #[jolt::provable] macro:
#[jolt::provable(
    max_input_size = 4096,
    max_output_size = 4096,
    max_trusted_advice_size = 0,
    max_untrusted_advice_size = 0,
    stack_size = 4096,
    heap_size = 1_000_000,
    max_trace_length = 1_000_000,
)]
fn my_function(input: u32) -> u32 {
    // implementation
}
These attributes configure the memory layout embedded in the preprocessing:
  • max_input_size - Maximum size of serialized public inputs
  • max_output_size - Maximum size of serialized outputs
  • max_trusted_advice_size - Maximum size of trusted advice
  • max_untrusted_advice_size - Maximum size of untrusted advice
  • stack_size - Guest program stack size
  • heap_size - Guest program heap size
  • max_trace_length - Maximum execution trace length

Performance Considerations

One-Time Cost

Preprocessing is a one-time cost for a given program. Once computed, the preprocessing can be reused for any number of proofs with different inputs.

Computation Time

Preprocessing time depends on:
  • Program size (bytecode length)
  • max_trace_length parameter
  • Memory configuration sizes
Typically takes seconds to minutes for moderate-sized programs.

Storage Size

Verifier preprocessing is typically much smaller than prover preprocessing, making it suitable for:
  • Embedding in verifier applications
  • Distribution to multiple verifiers
  • Long-term storage

Generated From

For a function annotated with #[jolt::provable]:
#[jolt::provable]
fn fib(n: u32) -> u32 {
    // implementation
}
The macro generates:
  • preprocess_shared_fib
  • preprocess_prover_fib
  • preprocess_verifier_fib
  • verifier_preprocessing_from_prover_fib
  • compile - Compiles the guest program required for preprocessing
  • prove - Uses prover preprocessing to generate proofs
  • verify - Uses verifier preprocessing to verify proofs
  • memory_config - Returns the memory configuration

Build docs developers (and LLMs) love