jolt
Rustで記述された関数を、Joltという技術を用いてゼロ知識証明で保護し、関数の処理内容を秘匿したまま実行できるようにするSkill。
📜 元の英語説明(参考)
Wrap a Rust function in a Jolt zero-knowledge proof
🇯🇵 日本人クリエイター向け解説
Rustで記述された関数を、Joltという技術を用いてゼロ知識証明で保護し、関数の処理内容を秘匿したまま実行できるようにするSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o jolt.zip https://jpskill.com/download/8072.zip && unzip -o jolt.zip && rm jolt.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8072.zip -OutFile "$d\jolt.zip"; Expand-Archive "$d\jolt.zip" -DestinationPath $d -Force; ri "$d\jolt.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
jolt.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
joltフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
発動条件: ユーザーが「この Jolt を証明可能にする」、「これを Jolt でラップする」、「これを Jolt で証明する」、「これに ZK 証明を追加する」、「これをゼロ知識にする」、「これを証明可能にする」、「これを jolt-ify する」と言う場合。
ステップ 1 — 証明する計算を特定する
純粋で決定的な Rust 関数を探します。入力があり、結果が出力され、I/O や副作用がないものです。明確でない場合は、次のように尋ねます。
「どの関数を証明可能にすべきですか? I/O や副作用のない純粋な Rust 関数である必要があります。」
ゲストコードを記述する前に、ターゲット関数とそのモジュールパス全体が pub であることを確認します。そうでない場合は、ライブラリソースで pub にし(推奨 - ライブラリを証明します)、ユーザーに確認し、ライブラリを変更したくない場合はインライン化が代替手段であることを伝えます。
ステップ 2 — シグネチャを分析して適合させる
ゲストは実際にはヒープを持っています。Vec、String、アロケーション型は本体内で自由に動作します。制約はパラメータ境界にあります。std モードは完全な serde を使用します(Vec/String はパラメータとして問題ありません)。no_std は serde_core を使用します(Vec パラメータは不可、配列はサイズ 32 に制限されます)。必要なものだけを適合させます。
| 問題 | 解決策 |
|---|---|
no_std の Vec<T> パラメータ |
[T; N], len: u32 — または std モードに切り替えます |
no_std の N > 32 の [T; N] |
複数のパラメータに分割します(serde_core の配列サイズ制限) |
usize |
u32 (ゲストは 32 ビット) |
f32 / f64 |
固定小数点整数 (例: i64 * 1_000_000) — RV64IMAC には FPU がありません |
std::io, std::net |
ゲストで実行できません — 説明して停止します |
| 非決定性 | シード/タイムスタンプを明示的な入力として渡します |
ビルドモード: ライブラリの Cargo.toml を読みます。ライブラリが std を必要とする場合、または例を単純にする場合(例: Vec/String をパラメータとして使用する場合)は、std モードを使用します。No_std は選択肢であり、デフォルトではありません。
ステップ 3 — Jolt をインストールする
jolt --version # インストールされているか確認
cargo install --git https://github.com/a16z/jolt --force jolt # インストールされていない場合
ステップ 4 — スキャフォールド
既存の Rust ライブラリリポジトリ内にある場合は、次のように提案します。
「ここに
<library-name>-jolt/を作成し、証明スキャフォールドを作成し、ライブラリをパス依存関係としてインポートします。よろしいですか?」
jolt new <project-name> # standard モード
jolt new <project-name> --zk # PrivateInput + BlindFold サポート付き
これにより、fib の例を含むワークスペースが生成されます。src/main.rs と guest/src/lib.rs 全体で fib → <fn> に名前を変更して置き換えます。ルートの Cargo.toml の [patch.crates-io] ブロックを保持します(必要な arkworks パッチ)。
ステップ 5 — ゲストを記述する (guest/src/lib.rs)
no_std モード (デフォルト):
#![cfg_attr(feature = "guest", no_std)]
extern crate alloc; // ヒープは常に利用可能
#[jolt::provable]
fn <fn>(<params>) -> <ret> { ... }
std モード — guest/Cargo.toml 内:
jolt = { package = "jolt-sdk", git = "https://github.com/a16z/jolt", features = ["guest-std", "thread", "stdout"] }
rayon/parallel の場合は "thread" を、println! の場合は "stdout" を含めます。lib ファイルに cfg_attr は不要です。
マクロパラメータ — #[jolt::provable] を単独で使用します。理由がある場合にのみパラメータを追加します。
| パラメータ | デフォルト | いつ変更するか | 値の選択方法 |
|---|---|---|---|
stack_size |
4096 | stack overflow |
8388608 (8 MB、Linux のデフォルトと一致) から開始します。最適化パスで削減します。 |
max_trace_length |
2^22 | max_trace_length exceeded |
analyze_<fn> を実行して実際のサイクル数を取得し、次の 2 のべき数に切り上げます。証明時間とメモリはこれに比例して増加します — ステップ 9 で絞り込みます。 |
heap_size |
32 MB | heap allocation failed |
ピーク時のライブアロケーションを見積もり、失敗するまで半分にし、元に戻します。 |
証明者のみの入力 — 暗号化によるプライバシーが必要かどうかに応じて、2 つのオプションがあります。
jolt::UntrustedAdvice<T>— 証明者のみ。検証者 API から除外されますが、値は証明から回復できる可能性があります。jolt::PrivateInput<T>— 同じ基になる型。BlindFold を介して値を暗号化的に隠すべきであることを示します (ホストでzkが必要で、ゲストでは不要です)。
#[jolt::provable]
fn my_fn(public: u64, secret: jolt::UntrustedAdvice<[u8; 32]>) -> bool {
let secret = *secret;
// ...
}
ホストの証明呼び出し: prove(..., UntrustedAdvice::new(val))。生成された検証者のシグネチャはアドバイスを完全に省略します。ホストに use jolt_sdk::UntrustedAdvice; を追加します。
PrivateInput<T> の場合は、ホストでのみ zk を有効にします (ステップ 7 を参照)。マクロはコンパイル時にこれを強制します。
TrustedAdvice<T> は、サードパーティによってコミットされたデータの代替手段です。commit_trusted_advice_<fn>(...) ホスト呼び出しが必要で、コミットメントは検証者に渡されます。
依存関係 — guest/Cargo.toml に追加します。既存のリポジトリをラップする場合は、<library> = { path = "../.." } を追加します。ライブラリがサポートしていることがわかっている場合を除き、default-features = false は避けてください。無効になっているデフォルト機能は、欠落しているオプションの依存関係を参照する条件付きコンパイルモジュールを公開する可能性があります。暗号化の場合は、jolt-inlines-sha2、jolt-inlines-keccak256、jolt-inlines-secp256k1 を優先します。
複数の関数 — 各 #[jolt::provable] は、独立した compile_*、preprocess_*、build_prover_*、build_verifier_* API を生成します。
アドバイス関数 — 証明の外部で実行する必要がある高価な証拠計算の場合は、ゲストで #[jolt::advice] を使用します。関数はホスト/証明者で実行されます。ゲストは jolt::check_advice_eq!(computed, expected) で結果を安価に検証します。
サイクル追跡 — ゲストのセクションを計測して、セクションごとのサイクル数を測定します (証明者のログに表示されます)。
use jolt::{start_cycle_tracking, end_cycle_tracking};
start_cycle_tracking("my section");
// ... 測定するコード ...
end_cycle_tracking("my section");
ステップ 6 — ホストを記述する (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 pr 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
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 proofjolt::PrivateInput<T>— same underlying type, signals that values should be cryptographically hidden via BlindFold (requireszkon 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_<fn>(target_dir);
let shared = guest::preprocess_shared_<fn>(&mut program);
let prover_prep = guest::preprocess_prover_<fn>(shared.clone());
let verifier_setup = prover_prep.generators.to_verifier_setup();
let verifier_prep = guest::preprocess_verifier_<fn>(shared, verifier_setup, None);
let prove = guest::build_prover_<fn>(program, prover_prep);
let verify = guest::build_verifier_<fn>(verifier_prep);
let t = Instant::now();
let (output, proof, io) = prove(<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(<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 belowmax_trace_lengthwe 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_lengthto 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
}
4. 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" }
5. 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.