jolt

Invoke when the user says: "make this Jolt provable", "wrap this in Jolt", "prove this with Jolt", "add ZK proofs to this", "make this zero-knowledge", "make this provable", "jolt-ify this".

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "jolt" with this command: npx skills add a16z/jolt/a16z-jolt-jolt

Invoke when the user says: "make this Jolt provable", "wrap this in Jolt", "prove this with Jolt", "add ZK proofs to this", "make this zero-knowledge", "make this provable", "jolt-ify this".

Step 1 — Identify the computation to prove

Look for a pure, deterministic Rust function — inputs in, result out, no I/O or side effects. If not obvious, ask:

"What function should I make provable? It needs to be a pure Rust function with no I/O or side effects."

Before writing any guest code, verify the target function and its entire module path are pub . If not, make it pub in the library source (preferred — we're proving the library) and confirm with the user, noting that inlining is an alternative if they'd rather not modify the library.

Step 2 — Analyze and adapt the signature

The guest has a real heap — Vec , String , alloc types work freely inside the body. The constraint is at the parameter boundary: std mode uses full serde (Vec/String as params fine); no_std uses serde_core (no Vec params, arrays capped at size 32). Only adapt what's necessary:

Issue Resolution

Vec<T> param in no_std [T; N], len: u32 — or switch to std mode

[T; N] where N > 32 in no_std Split across multiple params (serde_core array size limit)

usize

u32 (guest is 32-bit)

f32 / f64

Fixed-point integer (e.g. i64 * 1_000_000 ) — RV64IMAC has no FPU

std::io , std::net

Cannot run in guest — explain and stop

Non-determinism Pass seed/timestamp as explicit input

Build mode: read the library's Cargo.toml . Use std mode if the library requires std, or if it makes the example simpler (e.g. Vec/String as params). No_std is a choice, not the default.

Step 3 — Install Jolt

jolt --version # check if installed cargo install --git https://github.com/a16z/jolt --force jolt # if not

Step 4 — Scaffold

If inside an existing Rust library repo, propose:

"I'll create <library-name>-jolt/ here with the proof scaffold and import your library as a path dependency. Sound good?"

jolt new <project-name> # standard mode jolt new <project-name> --zk # with PrivateInput + BlindFold support

This generates a workspace with a fib example — replace it by renaming fib → <fn> throughout src/main.rs and guest/src/lib.rs . Preserve the [patch.crates-io] block in the root Cargo.toml (required arkworks patches).

Step 5 — Write the guest (guest/src/lib.rs )

no_std mode (default):

#![cfg_attr(feature = "guest", no_std)] extern crate alloc; // heap always available

#[jolt::provable] fn <fn>(<params>) -> <ret> { ... }

std mode — in guest/Cargo.toml :

jolt = { package = "jolt-sdk", git = "https://github.com/a16z/jolt", features = ["guest-std", "thread", "stdout"] }

Include "thread" for rayon/parallel, "stdout" for println! . No cfg_attr needed in the lib file.

Macro parameters — use #[jolt::provable] bare; only add parameters when you have a reason:

Parameter Default When to change How to pick a value

stack_size

4096 stack overflow

Start at 8388608 (8 MB, matches Linux default); reduce in the optimization pass.

max_trace_length

2^22 max_trace_length exceeded

Run analyze_<fn> to get actual cycle count, round up to next power of 2. Proving time and memory scale with this — tighten in Step 9.

heap_size

32 MB heap allocation failed

Estimate peak live allocations; halve until it fails, then double back.

Prover-only inputs — two options depending on whether you need cryptographic privacy:

  • jolt::UntrustedAdvice<T> — prover-only; excluded from the verifier API but values may be recoverable from the proof

  • jolt::PrivateInput<T> — same underlying type, signals that values should be cryptographically hidden via BlindFold (requires zk on the host, not the guest)

#[jolt::provable] fn my_fn(public: u64, secret: jolt::UntrustedAdvice<[u8; 32]>) -> bool { let secret = *secret; // ... }

Host prove call: prove(..., UntrustedAdvice::new(val)) . The generated verifier signature omits the advice entirely. Add use jolt_sdk::UntrustedAdvice; to the host.

For PrivateInput<T> , enable zk on the host only (see Step 7). The macro enforces this at compile time.

TrustedAdvice<T> is the alternative for data committed by a third party — it requires a commit_trusted_advice_<fn>(...) host call and the commitment is passed to the verifier.

Dependencies — add to guest/Cargo.toml . When wrapping an existing repo, add <library> = { path = "../.." } . Avoid default-features = false unless you know the library supports it — disabled default features can expose conditionally-compiled modules that still reference missing optional deps. For crypto, prefer jolt-inlines-sha2 , jolt-inlines-keccak256 , jolt-inlines-secp256k1 .

Multiple functions — each #[jolt::provable] generates independent compile_* , preprocess_* , build_prover_* , build_verifier_* APIs.

Advice functions — for expensive witness computation that should run outside the proof, use #[jolt::advice] in the guest. The function runs on the host/prover; the guest verifies the result cheaply with jolt::check_advice_eq!(computed, expected) .

Cycle tracking — instrument sections of the guest to measure per-section cycle counts (visible in the prover log):

use jolt::{start_cycle_tracking, end_cycle_tracking};

start_cycle_tracking("my section"); // ... code to measure ... end_cycle_tracking("my section");

Step 6 — Write the host (src/main.rs )

use std::time::Instant; use tracing::info;

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

let target_dir = "/tmp/jolt-guest-targets";
let mut program = guest::compile_&#x3C;fn>(target_dir);
let shared = guest::preprocess_shared_&#x3C;fn>(&#x26;mut program);
let prover_prep = guest::preprocess_prover_&#x3C;fn>(shared.clone());
let verifier_setup = prover_prep.generators.to_verifier_setup();
let verifier_prep = guest::preprocess_verifier_&#x3C;fn>(shared, verifier_setup, None);
let prove = guest::build_prover_&#x3C;fn>(program, prover_prep);
let verify = guest::build_verifier_&#x3C;fn>(verifier_prep);

let t = Instant::now();
let (output, proof, io) = prove(&#x3C;inputs>);
info!("Prover runtime: {} s", t.elapsed().as_secs_f64());

// io.panic is true if the guest panicked; the verifier checks it matches the proof
let is_valid = verify(&#x3C;inputs>, output, io.panic, proof);
info!("output: {:?}", output);
info!("valid: {is_valid}");
assert!(is_valid);

}

For multiple functions, replicate the block per function. To measure cycles before proving: guest::analyze_<fn>(<inputs>).write_to_file("summary.txt".into()).unwrap() .

Step 7 — Run

Before running, estimate peak memory from max_trace_length (conservative worst-case):

max_trace_length Peak memory

≤ 2^23 < 10 GB

2^24 ~15 GB

2^25 ~32 GB

2^26 ~42 GB

2^27 ~81 GB

2^28 ~99 GB

If max_trace_length is 2^24 or above, warn the user and ask how to proceed:

"This may require ~X GB of RAM. I can: (a) run analyze_<fn> first to get the actual cycle count — if it's well below max_trace_length we can lower it and reduce memory significantly, or (b) proceed directly. Which do you prefer?"

RUST_LOG=info cargo run --release

For full zero-knowledge (hides witness via BlindFold protocol), enable zk in both crates. Use jolt new --zk to scaffold a ZK project, or add manually:

Host Cargo.toml :

jolt-sdk = { git = "https://github.com/a16z/jolt", features = ["host", "zk"] }

Guest Cargo.toml :

jolt = { package = "jolt-sdk", git = "https://github.com/a16z/jolt", features = ["zk"] }

In the host, pass BlindfoldSetup to verifier preprocessing:

let blindfold_setup = prover_prep.blindfold_setup(); let verifier_prep = guest::preprocess_verifier_<fn>(shared, verifier_setup, Some(blindfold_setup));

Preprocessing runs once on first invocation and is not included in "Prover runtime". Diagnose failures:

Error Fix

max_trace_length exceeded

Add max_trace_length = N (tight power of 2 — proving time scales with this)

heap allocation failed

Add heap_size = N

stack overflow

Increase stack_size ; start at 8388608 (8 MB) if not already set

Illegal instruction

Rewrite floats as fixed-point

could not find crate

Find no_std alternative or switch to std mode

does not implement Serialize

Add #[derive(serde::Serialize, serde::Deserialize)]

Step 8 — Summarize

Tell the user: what function was made provable, what type adaptations were applied and why, std or no_std mode, and how to run it.

Once the proof runs end-to-end, always offer a performance optimization pass:

"The proof works! Want me to optimize it? I can tighten max_trace_length to reduce memory and proving time, profile which sections dominate cycle count, and offload expensive witness computation."

Step 9 — Optimize (offer after Step 8 succeeds)

Work through these in order:

  1. Tighten max_trace_length — run guest::analyze_<fn>(<inputs>) , find the actual cycle count, set max_trace_length to the smallest power of 2 above it. Proving time and peak memory are both proportional — a 2× reduction is a 2× speedup.

  2. Find the bottleneck — add start_cycle_tracking / end_cycle_tracking (see Step 5) around major sections and run analyze_<fn> again. Focus on whichever section consumes >50% of cycles.

  3. Offload expensive witness computation — if a section is expensive to compute but cheap to verify (sorting, hashing, witness generation), convert it to #[jolt::advice] . The advice function runs on the host outside the proof; the guest only verifies the result:

#[jolt::advice] fn sort_array(input: &[u64]) -> jolt::UntrustedAdvice<Vec<u64>> { let mut v = input.to_vec(); v.sort_unstable(); v }

#[jolt::provable] fn my_fn(input: &[u64]) -> bool { let adv = sort_array(input); let sorted = &*adv; // O(n) verification: sorted order + length jolt::check_advice!(sorted.windows(2).all(|w| w[0] <= w[1])); jolt::check_advice!(sorted.len() == input.len()); true }

  1. Use crypto inlines — for SHA-2, Keccak, secp256k1, replace standard crate calls with jolt-inlines-* (constraint-native, fraction of the cycle cost):

jolt-inlines-sha2 = { git = "https://github.com/a16z/jolt" }

  1. Trim stack_size and heap_size — over-allocation doesn't cost cycles but does increase peak prover memory. Lower to actual usage once max_trace_length is tight.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Research

learn-anything-in-one-hour

Teach users any new skill/knowledge X in ~1 hour using a fixed 4-step workflow optimized for complete beginners, focusing on 80/20 rule for maximum value in minimum time. Triggers when user asks to learn something new quickly, or mentions "learn X in one hour".

Archived SourceRecently Updated
Research

X/Twitter Research

# X/Twitter Research Skill

Archived SourceRecently Updated
Research

council

Convene the Council of High Intelligence — multi-persona deliberation with historical thinkers for deeper analysis of complex problems.

Archived SourceRecently Updated