Rand Protocol
ドキュメント
ガイド

zkVMのしくみ

Rand が、入力を明かすことなくプログラムが正しく実行されたことをどのように証明するかを説明します。RISC-V マシン、9つのテーブルに分割された実行トレース、テーブル同士をつなぐバス、多項式恒等式としての規則、コミットメント、FRI、Fiat-Shamir、ゼロ知識性の由来、validator が検証する内容、そして FRI とハッシュの各パラメータがその値である理由を扱います。

このページでは、Rand 上のすべての証明を支えるマシンについて説明します。何を証明するのか、プログラムの実行がどのようにしてバリデータがミリ秒で検証できるものになるのか、そしてプライバシーがどこから生まれるのかを扱います。証明のコストとウォレットによる生成方法については Proofs を、パラメータとハッシュ構成については Cryptography を、プログラムが前提とする命令セットについては The RISC-V ISA を参照してください。

すべての証明が行う唯一の主張

zkVM は、チェーンが実行するあらゆるプログラムについて、ただ一種類の主張を証明します。

ダイジェストが hc であるプログラムが、base_pc から開始され、H_IN でコミットされた非公開入力と H_PUB でコミットされた公開入力セグメントを用いて実行され、2ᵗ − 1 サイクル以内に停止し、消費したガスは高々 GAS_LIMIT であり、次の 8 個の出力ワードを書き込んだ。

シールドされた送金とは、固定された 1 つのプログラム、すなわち隠蔽アセットバンドルのゲストに関するこの主張のことです。その hc は genesis ファイルに hc_bundle として固定されています。チェーン 17 以降、この証明は送金ごとに 2 回行われます。認可ゲスト(genesis ファイルの hc_auth)に関する小さな 2 つ目の証明が、送信者が使用鍵を保持していることを示します(Proofs)。機密呼び出しは、デプロイ済みプログラムに関する同じ主張です。すべてに対して、マシンも検証器も制約の集合も 1 つずつしかありません。したがってプログラムのデプロイは暗号処理ではなくデータ操作(そのワードと hc を保存すること)であり、プログラムごとの回路もトラステッドセットアップもありません。

証明システムは Plonky3 で構築された STARK です。必要なのはハッシュ関数だけです。セットアップの儀式も楕円曲線も使わないため、Shor のアルゴリズムで破られるものがありません。

パイプライン

 secret input ──► run the program ──► execution trace ──► polynomials ──► commit ──► FRI ──► proof
 (keys,           (RV32IM emulator)   (nine tables,      (one per       (8× code,   (80 queries,
  notes, paths)                        one row per step)  column)        Merkle)     20-bit grinding)

 every validator:  recompute the challenges from the Merkle roots, open 80 random positions,
                   check the rules there  →  ≈ 16 ms

上段は送信者のマシンで一度だけ実行され、バンドルでは約 100 秒、隣の認可証明では約 7 秒かかります。下段はすべてのバリデータで、公開フィールドと証明だけから実行されます。

1. プログラムを実行してすべてを書き留める

マシンは 32 ビットの RISC-V プロセッサで、FENCE、CSR 命令、EBREAK を除いた RV32IM です(The RISC-V ISA)。証明者はまずエミュレータ上でプログラムを実行し、クロックの刻みごとに 1 行を記録します。この記録が実行トレースです。トレースを持つ者は誰でも実行を一歩ずつ再生できます。このスキームの残りの部分は、検証者がトレースを持たなくてもそれが正しいと納得できるようにするために存在します。

プログラムは 8 つのシステムコールだけを通じて外部とやり取りし、それ以外の手段はありません。

システムコール番号動作
HALT0停止します。8 個の出力ワードが確定します
WRITE_OUTPUT18 個の公開出力ワードのうち 1 つを設定します
READ_INPUT2非公開入力のワードを 1 つ読み取ります
POSEIDON23メモリ上のバッファをその場でハッシュします
KECCAK4Keccak-f[1600] 置換を 1 回実行します
SHA2565SHA-256 圧縮を 1 回実行します
READ_PUBLIC6公開入力セグメントのワードを 1 つ読み取ります
POSEIDON2_LEN7メッセージ長をハッシュに束縛した POSEIDON2(制約セット 7)

ノートコミットメント、ナリファイア、Merkle パスはマシンに組み込まれていません。これらは、ドメインタグ付きのメッセージをメモリ上に用意して POSEIDON2 を呼び出す、通常のゲストコードです。このシステムコールはパディングを行わないため、[a] と [a, 0] は同じハッシュになります。同梱のゲストは固定長のメッセージに対してのみこれを呼び出します。長さが変わりうるデータをハッシュする新しいプログラムは POSEIDON2_LEN を使います(The RISC-V ISA)。

中断はありません

プロセッサには終了を拒否する命令がないため、プログラムは検査に失敗しても停止できません。そこでバンドルゲストは代わりに失敗を記録します。検査に失敗するたびに bad というレジスタに 1 を OR し、bad は出力ダイジェストにハッシュされる最後のワードになります。

D = H(64; anchor, nf₀..₃, cm₀..₃, fee, burn_a, burn_r, burn_asset, time, c, bad)

c は認可コミットメントで、認可証明も公開します(チェーン 17 以降。チェーン 14 から 16 のダイジェストには c がありませんでした)。

チェーンは D のプリイメージを送信者から受け取ることはありません。トランザクションの公開フィールドからプリイメージを再構築し、自ら bad = 0 に設定してハッシュし、比較します。不正を働く者は、検査が失敗した実行についても完全に有効な証明を作ることができますが、その実行が出力したのは H(…, 1) であり、チェーンが探しているのは H(…, 0) です。両者を一致させるにはハッシュ衝突が必要です。

2. トレースを 9 つのテーブルに分割する

プロセッサ全体を 1 つのテーブルで記述すると幅が無駄に広くなるため、トレースは専門化されたテーブルに分割され、1 つのバッチとしてまとめて証明されます。

テーブルティア t での行数記録する内容
program宣言値、2⁷ 以上プログラムの命令ワード。回路内でデコードされ、CPU がフェッチできるものを固定します
cpu2ᵗサイクルごとに 1 行。その前に、プログラム、非公開入力、公開セグメントをハッシュする行が置かれます。35 個の公開値を保持します
memory宣言値、2ᵗ⁺² 以上レジスタと RAM。(空間, アドレス, 時刻)の順に整列されます
alu2ᵗ⁺¹すべての算術、論理、シフト、比較、乗算、除算。バイトリムで表現されます
range、nibble各 256固定のルックアップテーブル:バイト、2 のべき乗、4 ビットの AND/OR/XOR
poseidon22ᵗ⁺²ハッシュ置換。ラウンドごとに 1 行
input宣言値、2⁷ 以上コミットされた非公開入力ワード
public宣言値、2⁷ 以上公開セグメントのワード(バンドルの場合はトランザクションへの束縛)

さらに 2 つのテーブル keccak と sha256 は、プログラムがそれらを呼び出した証明にのみ存在し、存在する場合はそれぞれ少なくとも 2⁷ 行を持ちます。バンドルゲストはどちらも使いません。宣言されるすべてのテーブルに課される 2⁷ 行の下限は、制約セット 7 とともに導入されました(セクション 9)。

ティア t は 10、12、14、16、18、20 のいずれかで、テーブルのサイズを決めます。すべてのバンドルはティア 14 で証明されます。これは 16,384 行の CPU テーブルで、ゲストが使うのはそのうち約 12,400 行です。認可ゲストはティア 10 で 250 サイクルを実行します。残りはパディングです。そのため証明はプログラムがどのティアを必要としたかを明かしますが、そのティア内で何ステップかかったかは明かしません(Tiers)。

RAM を検証可能にしているのはメモリテーブルです。アドレス、次いで時刻の順に整列されているため、「読み取りはそのアドレスに最後に書き込まれた値を返す」という条件が、隣り合う 2 行の間の規則になります。

3. バスでテーブルをつなぐ

CPU が使った和を ALU が計算した和と同じだと、検証者はどのようにして知るのでしょうか。それがバスです。CPU は加算が必要になると、メッセージ (op, a, b, c) を送信します。ALU は計算する行ごとに 1 つのメッセージを受信します。マシンには 17 本のバスがあります。

バス接続
PROGRAM、PROGRAM_WORDcpu ← program:命令フェッチと、プログラムダイジェストが吸収するワード
MEMORYcpu ↔ memory:すべての読み取りと書き込み
ALUcpu → alu
RANGE8、POW2、AND4、OR4、XOR4固定テーブルに対するバイト範囲検査、シフト、ビット演算
POSEIDON2cpu → poseidon2:置換ごとに 1 つのメッセージ
INPUT_DIGEST、INPUT_READcpu ← input:入力ダイジェストが吸収するワードと、プログラムによる読み取り
PUBLIC_DIGEST、PUBLIC_READcpu ← public:公開セグメントに対する同じ組
KECCAK、SHA256cpu → 任意のハッシュテーブル
BLINDすべてのテーブル → 固定の巡回順で次のテーブル:最初の行にそれぞれ 1 つのランダム値(制約セット 7)

バスは**対数微分引数(LogUp)**で検査されます。テーブルがコミットされた後にランダム値 α が引かれ、各テーブルは、送信または受信するメッセージについて ±1/(α − m) の累積和を保持します。この和が厳密に打ち消し合うのは、送信されたすべてのメッセージが受信されている場合、つまり両側で同じ多重集合になっている場合に限られます。偽造されたメッセージが 1 つあれば残差が生じ、ランダムな α によってそれは圧倒的な確率で露呈します。メモリバスには、近い親戚である多重集合の置換検査を使います。

各テーブルは累積和の最終値であるターミナルを公開し、検証者はターミナルの合計がゼロになることを検査します。制約セット 6 まで、ターミナルはそのテーブルの内容から検査可能な関数として決まっていました。制約セット 7 以降は、すべてのテーブルが 5 つの追加列を持ち、最初の行で 1 つのランダム値を送信し、BLIND バス上で隣のテーブルのランダム値を受信します。各ターミナルはチャレンジ体の一様な元だけずれ、和はゼロのままです。

ダイジェストバスと読み取りバスは、意図的に別々の組になっています。LogUp はコンシューマ単位ではなくメッセージ単位でバランスを取るため、バスを 1 本に共有すると、証明者はダイジェストからあるワードを落としつつ、そのワードの読み取りを成功させることができてしまいます。

4. トレースを外部の世界に結びつける

トレースからは 35 個の公開値が出力されます。すべて CPU テーブルにあります。

値の数内容
1エントリ pc
1ティア
8出力ワード(バンドルの場合はダイジェスト D)
8hc、プログラムダイジェスト。プログラムテーブルのワードからトレースの内部で計算されます
8H_IN、非公開入力のダイジェスト。先頭に新しい 128 ビットのソルトを付けた入力に対して計算されます
8H_PUB、公開セグメントのダイジェスト。ソルトなし
1GAS_LIMIT、証明者が宣言するガスの上限(制約セット 8)

hc は回路内で計算されるため、別のプログラムについての証明をバンドルのゲストについての証明として通すことはできません。検証者は命令ワードを一切目にせず、hc を自分が期待するダイジェストと比較します。H_IN はソルト付きなので、推測できる相手に対しても、非公開入力を明かさずに束縛します。H_PUB はソルトなしなので、検証者はすでに持っているワードから再計算できます。バンドルの場合、そのワードはトランザクションバインディング、すなわち証明バイト列を空にしたトランザクションに対する blake3("rand-tx-bind-1" ‖ chain_id ‖ tx) です。そのため、宛先、メモ、エンベロープ、チェーン ID のいずれかが異なるトランザクションに証明をコピーしても、検証は通りません。chain 20(genesis の binding_domain: 1)では blake3("rand-tx-bind-2" ‖ genesis_hash ‖ chain_id ‖ tx) となり、同じチェーン ID であっても別のチェーンでは証明が検証されません。このワードは公開入力であるため、回路には何も変更がありません。auth 証明も同じ 8 ワードに対して作られます。chain 20 の呼び出し証明も同じ方法で束縛されます。そのセグメントは、プログラムのデプロイ時の公開ワードに、呼び出しバインディングの 8 ワード(rand-call-bind-2、chain 20 より前は rand-call-bind-1)が続いたものです。したがって、別の手数料バンドルのもとにコピーした呼び出し証明は検証されません。

ガスは、実行の重さを測るマシンの尺度で、制約セット 8 以降は回路に組み込まれています。CPU テーブルには GAS 列があり、1 から始まって、1 行ごとに 1、Poseidon2 の absorb 行で 3、SHA256 行で 64、KECCAK 行で 192 が加算されます。HALT 行で回路は、差分の 4 つの範囲検査付きリムを通じて GAS ≤ GAS_LIMIT を証明します。このリムは開示されません。上限は公開されていますが、実行が消費したガスは公開されないからです。検証者は、鍵を構築する前に、証明自身のヘッダーが必要としうる量を超える上限を拒否します。

gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ / 32) + 63·(2ˢ / 64)

ここで k と s は keccak テーブルと sha256 テーブルの宣言されたログ高さで、テーブルが存在しない場合、その項はゼロです。ハッシュを使わないティア 10 のヘッダーの上限は 1,279、ティア 14 のヘッダーの上限は 20,479 です。チェーンは、呼び出しを宣言された上限で課金します(手数料)。

5. 規則を多項式の恒等式に変える

すべての列は、Goldilocks 体、つまり p = 2⁶⁴ − 2³² + 1 を法とする整数の並びです。n 個の数の並びは、次数が n 未満の多項式と同じものです。その多項式とは、数の並びを体の部分群 {1, ω, ω², …} 上に並べたときに、それらを通る多項式です。Goldilocks には 2³² までの 2 のべき乗サイズの部分群がすべて存在するため、この変換は高速フーリエ変換で行えます。

すると、マシンの規則は列多項式の間の等式になります。「プログラムカウンタは、命令がジャンプしない限り 4 ずつ進む」という規則は、模式的には次のようになります。

is_jump(x)·(pc(ωx) − target(x)) + (1 − is_jump(x))·(pc(ωx) − pc(x) − 4) = 0

これは部分群のすべての x について成り立ち、pc(ωx) は「次の行」を意味します。あるテーブルのこうした規則の全体が、そのテーブルの AIR(代数的中間表現)です。CPU と ALU の規則は次数が 8 に達し、それ以外は 2 から 4 にとどまります。

部分群のすべての点でゼロになる多項式は、Z(x) = xⁿ − 1 で割り切れます。証明者はすべての規則をランダムな重みで 1 つの多項式 C(x) にまとめ、Z(x) で割ります。すべての規則がすべての行で成り立てば、商は本物の低次多項式になります。1 つの規則が 1 行でも破れていれば、そうなりません。「16,000 サイクルの実行のすべてのステップが正しかったか」という問いは「この関数は低次多項式に近いか」という問いに変わり、これはランダムに選んだ少数の値を調べることで答えられます。

6. 質問が分かる前にコミットする

証明者は、検証者のランダムな質問を見る前に自分の多項式を固定しなければなりません。そうしないと、答えを都合よく作れてしまいます。証明者は、各列をテーブルの8 倍の大きさのドメイン(ブローアップ 8、符号化率 1/8)上で評価し、その評価値に対する Merkle ルートを公開します。この冗長性は Reed–Solomon 符号です。低次多項式は値の任意の 8 分の 1 で決まるため、トレースの 1 行でも書き換える不正者は、整合性を保つために拡張符号語のほとんどを書き換えなければならず、ランダムな抜き取り検査がその破損に当たります。

Merkle ツリーには、Goldilocks 上の Poseidon2 を使います。幅は 8、S-box は x⁷、完全ラウンド 4、部分ラウンド 22、完全ラウンド 4、ダイジェストは 4 つの体要素(約 256 ビット)です。すべての葉は 4 要素の新しい乱数とともにハッシュされるため、ルートからはその下にある値について何も分かりません。同じ置換が、マシン内部のノートコミットメントとナリファイアのハッシュになります。86 個のラウンド定数はノードのソース(poseidon2_constants.rs)にリテラルのテーブルとして書かれているため、ライブラリのアップグレードで変わることはありません。

7. FRI で抜き取り検査をする

ここで検証者は Merkle ルートを手にしています。検証者はまず、商をトレースに結びつけます。テーブルのドメイン外のランダムな点 z を選び、その点での各列の値と商の値を求め、C(z) = Q(z)·Z(z) を直接確認します。残るのは、コミットされた関数が本当に低次多項式であることの確認で、これを検査するのが FRI です。

FRI は折りたたみによって動作します。多項式を偶数部と奇数部に分けて f(x) = fₑ(x²) + x·fₒ(x²) とします。検証者からのランダムな β に対して、f′(y) = fₑ(y) + β·fₒ(y) は、半分の大きさのドメイン上で次数が半分になります。証明者は f′ にコミットし、新しい β を受け取って再び折りたたみます(Rand は 1 ラウンドあたり最大 8 で折りたたみます)。これを定数が残るまで繰り返し、その定数を平文で送ります。クエリは、最初の層でランダムな位置を 1 つ選んでそれを下へたどり、各層で、上で開示された値が下の値へ折りたたまれることを確認します。低次から遠い関数は、これらすべての約束を整合させ続けることができません。

Rand は 80 回のクエリを実行します。証明済みの近接ギャップ定理のもとでは、各クエリが不正者に残す成功確率は最大 9/16 なので、80 回のクエリで (9/16)⁸⁰ ≈ 2⁻⁶⁶·⁴ となります。実運用の STARK が拠り所とする ethSTARK 予想のもとでは、各クエリが残す確率は 1/8 で、これははるかに小さい値です。

8. 対話を取り除く

ここまでは対話として説明してきましたが、トランザクションは、どのバリデータも単独で検査できる 1 つのメッセージでなければなりません。Fiat–Shamir 変換は検証者を取り除きます。証明者は、各「ランダム」チャレンジを、それまでにコミットしたすべてのものを Poseidon2 のデュプレックススポンジでハッシュした値として計算します。どのコミットメントを変えても、それ以降のすべてのチャレンジが変わるため、証明者はチャレンジに合わせてコミットメントを選ぶことができません。

これによって残る隙間は、さらに 2 つの対策でふさぎます。

  • 拡大体。 2⁶⁴ 個の要素からなる体は、チャレンジを引くには小さすぎます。不正者は、チャレンジが多項式の少数の悪い値のどれかに当たればよく、クエリを何回実行しても約 N/2⁶⁴ の確率で成功します。そこで、トレースは Goldilocks にとどめたまま、すべてのチャレンジをその二次拡大体(約 2¹²⁸ 個の要素)から引きます。
  • グラインディング。 クエリ位置を導出する前に、証明者は、トランスクリプトとのハッシュが 20 個のゼロビットで始まるナンスを見つけなければなりません。これは約 100 万回のハッシュに相当します。誠実な証明者がこれを支払うのは 1 回だけです。クエリ位置を、偽造が外れるものになるまで引き直す不正者は、試行のたびにこれを支払うことになります。これは Rand 全体で唯一のプルーフ・オブ・ワークであり、コンセンサスとは無関係です。

合計すると、クエリによる 66.4 ビットにグラインディングの 20 ビットを加えて 86 ビットが証明済みとなり、予想のもとでは約 100 ビットで、その場合は拡大体の項が限界になります。

9. ゼロ知識はどこから来るのか

STARK はそれだけではゼロ知識ではありません。クエリ位置で開示される値はトレース多項式の値であり、何も手を打たなければ情報が漏れます。Rand の構成は 4 つの層を加えています。

  1. コミットされるすべての多項式はランダム化されます(Plonky3 のハイディング FRI は 4 つのランダムな符号語を混ぜ込みます)。そのため、開示される 80 個の値は、トレースを見たことのない者でも、分布として再現できます。
  2. すべての Merkle の葉はソルト付きなので、ルートと認証パスからは何も分かりません。
  3. 証明者の乱数は証明ごとにオペレーティングシステムから新しく取得されるため、同じ支払いの 2 つの証明は無関係に見えます。
  4. 宣言されたすべてのテーブルは少なくとも 2⁷ 行を持ち、すべての LogUp ターミナルはブラインド化されます(制約セット 7)。証明は各テーブルを 80 個のクエリ点と 2 個のドメイン外の点で開き、ハイディングコミットメントはテーブルの行数と同じ数のランダムな行を混ぜ込みます。そのため、テーブルが隠されるのは 82 行以上あるときに限られます。それを満たす最小の 2 のべき乗が 128 です。

レイヤー4は訂正です。2026-09-27のレビューでは、本番の証明から、入力ワードが31個以下の呼び出しの秘匿入力を復元できること、プログラムのフェッチ回数をその短いプログラムテーブルから読み取れること、そしてバンドルの実際の入力スロットをダミーと区別できること(ブラインドされていない終端によります)が判明しました。これに該当する証明は、chain 15以前で作られたものです。chain 16(制約セット7、分岐のないバンドルゲストを備えたもの)でこの3点はすべて塞がれ、chain 18から20までその修正を引き継いでいます。

保証は、ランダムオラクルモデルにおける統計的ゼロ知識です。証明が隠さないものは明確に決まっています。ティア、宣言されたテーブルの高さ、そしてガスリミットを含む公開値です。宣言された keccak または sha256 の高さが 0 であれば、その実行でそのハッシュが一度も使われなかったことを意味します。そのため、呼び出し証明のヘッダーからは、呼び出しがハッシュを使ったかどうかが分かり、下限を超える場合にはその回数も2倍以内の精度で分かります。バンドルの公開値とフィールドは、アンカー、4つのナリファイア、4つのコミットメント、手数料、バーン、バーン対象のアセット、時刻、認可コミットメント、そしてトランザクションバインディングです。送金で動かすアセットは秘匿入力であるため、zUSD の支払いと RAND の支払いはオンチェーン上で区別がつきません。hc は決定的なので、証明がバンドルゲストに関するものであること(価値を動かす証明はすべてそうです)、あるいは呼び出しの場合にどのデプロイ済みプログラムを実行したかは、誰でも判別できます。

10. バリデータが検証すること

Machine::verify_public(hc, words, proof) は、コストの低いものから順に次を実行します。

  1. 証明が宣言したティアとテーブルの高さに上限があることを(check_declared_heights)、それらからサイズを決める前に確認します。このため、不正なヘッダーのコストは数回の比較で済み、数秒かかる鍵の構築は発生しません。バンドルはどちらのハッシュテーブルも持たないティア14を宣言しなければならず、認可証明はティア10を宣言しなければなりません。
  2. 公開値は35個あり、それぞれが正規のフィールド要素で、pv[HC] = hc、pv[TIER] は証明のティアに等しく、すべての出力ワードは 2³² 未満で、GAS_LIMIT はヘッダーの gas_max 以下です。
  3. バッチ STARK は、プログラムごとではなく、シェイプ (tier, program, input, keccak, sha256, public heights) ごとにキャッシュされた検証鍵に対して検証されます。
  4. H_PUB は words のダイジェストに等しくなります。バンドルの場合、これはノード自身が計算したトランザクションバインディングです。

これらに先立ち、ノードは送信またはゴシップされたすべての証明について、正規のエンコーディングであるかを選別します。その FRI のフォールディングスケジュールは、ヘッダーが宣言する次数に対して正直なプロverが作るものと一致していなければなりません。fullnode v0.6.8(audit v6、ZKV-4)以降、この選別ではまず、25ビットを超える宣言次数を拒否します(MAX_HONEST_DEGREE_BITS。メモリテーブルの 2²⁴ という上限にゼロ知識のための倍化を加えたもので、受理される証明が持ちうる最大値です)。この上限が設けられる前は、巨大な次数を宣言するヘッダーによって、ノードがそのイベントループ上で膨大なフォールディングスケジュールを再導出させられ、送信者の望む限り占有される状態にありました。検証者はいずれにせよその証明を拒否するため、この上限が変えるのは拒否にかかるコストだけです。選別中のパニックは、ノードを停止させることなく、その1件のトランザクションを拒否するようになりました。

その後、台帳はバンドルのダイジェスト D を bad = 0 で再計算し、証明の8個の出力ワードと比較します。chain 20 ではさらに、バンドル証明の GAS_LIMIT がジェネシスの bundle_gas_limit である 20,479 と正確に一致すること、認可証明のそれが 1,279 であることを要求するため、すべての送金が同じ値を宣言します。鍵がウォームな状態であれば、検証全体にかかる時間は約 16 ms です。

パラメータとその選定理由

以下の数値はすべてコンセンサスの一部です。検証者がそれを検査し、他の値で作られた証明は検証を通らず、どれか1つを変更することは新しい制約セットとハードフォークを意味します(制約セット)。FRI プロファイルは、証明ではなくジェネシスファイル(chain 20 では fri_profile: production)によって固定されます。

パラメータ値この値にした理由
base fieldGoldilocks、p = 2⁶⁴ − 2³² + 1算術が数個のマシン命令で済む 64 ビット素数で、2³² までの 2 の冪サイズすべての乗法部分群を持つため、列を多項式に変換する処理は高速フーリエ変換になります。トレースがここにあるのは、これが安価な体だからです。
challenge field二次拡大体 F_p²(≈ 2¹²⁸)Goldilocks 自体からチャレンジを引くと、N/2⁶⁴ 程度の健全性の下限が残り、クエリ数をいくら増やしても下げられません。拡大体を用いることで、この項はトレース長 2²⁵ 行までで最大でも 2⁻¹⁰⁰ に抑えられます。大きな体のコストを負担するのはチャレンジだけで、トレースは負担しません。128 ビットが必要になった場合の余裕として、三次拡大体(≈ 2¹⁹²)を候補に挙げています。
blow-up8(レート ρ = 1/8、log_blowup = 3)理由は二つあります。CPU テーブルと ALU テーブルには次数 8 の制約があり、zero-knowledge フラグを立てると商の次数は 9 になります。これはブローアップ 8 が扱える上限であり、これより次数の高い制約を置けばブローアップ 16 が必要になります。また、各クエリが不正を検出する力はレートで決まります。証明済みでは 1 クエリあたり (1 + ρ)/2 = 9/16、予想では ρ = 1/8 です。ブローアップを大きくすればクエリ数は減らせますが、その代償として、プルーバーが計算しメモリに保持する低次拡大が、それに比例して大きくなります。
FRI queries80証明済みの健全性を約 86 ビットに保つための数です。かつては 27 に減らされており(制約セット 3 と 4)、予想される 100 ビットの目標(3·27 + 20 = 101)は満たしたものの、証明済みのビットは約 42 しか残りませんでした。2026-09-12 の zk 監査(指摘 ZM1)で 80 に戻されました。証明サイズの大半はクエリが占めており、バンドル証明が ≈ 1.50 MB になるのはそのためです。証明可能な 100 ビットには 97 クエリが必要です。
grinding20 ビット、クエリフェーズクエリ位置を引く前に、プルーバーはハッシュが 0 のビット 20 個で始まるナンスを見つけなければなりません。約 100 万回のハッシュ計算で、正直なプルーバーは一度だけ、不正者は位置を引き直すたびに支払うことになります。20 ビットを低コストで上乗せできます。グラインディングを増やせば(34 ビットなら証明可能な 100 も得られます)、正直な証明はすべて遅くなります。コミットフェーズにはグラインディングはありません。
folding arity1 ラウンドあたり最大 8(max_log_arity = 3)2 ではなく 8 でフォールディングすると FRI のレイヤー数が 3 分の 1 になり、各クエリが開く Merkle パスが減って証明が小さくなります。代わりに、レイヤーあたりの処理が少し増えます。
final polynomial定数(log_final_poly_len = 0)FRI は最後まで折り畳み、1 つの値を平文のまま送ります。検証者にとって最も単純な最終チェックです。
hash(回路内、Merkle、トランスクリプト)Poseidon2、幅 8、S-box x⁷、4 + 22 + 4 ラウンド、レート 4、キャパシティ 4体上の算術として安価になるよう作られたハッシュです。そのため、証明の内部でのハッシュ計算(コミットメント、ナリファイア、Merkle パス、プログラムダイジェスト)に要するのは、数千のビット演算ではなく行数です。S-box の指数は p − 1 = 2³²·3·5·17·257·65537 と共通因数を持ってはならず、7 は条件を満たす最小の値です。幅 8・レート 4 では 4 要素のダイジェスト、約 256 ビットが得られます。86 個のラウンド定数は、ノード内にリテラルのテーブルとして置かれています(poseidon2_constants.rs)。
digest width体要素 4 個(≈ 256 ビット)原像に関する用途(ナリファイア、コミットメント:Grover に対して 128 ビット)には十分です。一方、トランスクリプトで 128 ビットの量子衝突耐性を得るには足りません(2²⁵⁶/³ ≈ 2⁸⁵)。ホワイトペーパーの設計は 384 ビットのトランスクリプトハッシュを求めていますが、稼働中のチェーンはまだ採用していません。
hidingFRI にランダムなコードワード 4 個、Merkle の各リーフにソルト要素 4 個、証明ごとに新しい OS 乱数開かれた 80 個の位置と Merkle パスを公開値だけからシミュレートできるようにする最小限の構成で、これにより証明は統計的ゼロ知識になります。同じ支払いについて 2 回作った証明は、互いに無関係に見えます。
table floor宣言されたすべてのテーブルで 2⁷ 行(制約セット 7)証明は各テーブルについて 80 個のクエリ点と 2 個のドメイン外の点、合わせて 82 点を開き、h 行のテーブルは h 個のランダムな行でブラインドされます。64 行では足りず、128 が次の 2 の冪です。クエリ数が 126 を超える場合は 2⁸ が必要になります。
terminal blindテーブルごとに F_p² の値 1 つ、BLIND バス上(制約セット 7)LogUp の終端値は生のトレースから計算されるため、ブラインドがなければ、そのテーブルに対するソルトなしのコミットメントになってしまいます。ブラインドを拡大体の要素にしているのは、基礎体の座標が 1 つだけだと、シフトが薄い集合に閉じ込められ、候補のウィットネスをそれに対して試せてしまうからです。
gas weights1 行あたり 1、Poseidon2 absorb 行あたり 3、SHA256 行あたり 64、KECCAK 行あたり 192(制約セット 8)各重みは、その行が他のテーブルに引き込む行数を数えたものです。absorb 行は 32 行の置換 1 回、SHA256 行は 466 列の 64 行、KECCAK 行は 2,612 列の 32 行とメモリ 100 行です。
maximum constraint degree8上記のブローアップで決まります。各テーブルの次数はテストで固定されており、新しい制約が気付かないうちに上限を超えることはありません。
tiers10、12、14、16、18、20テーブルは 6 つの固定ステップでサイズが決まり、各ステップは前の 4 倍です。証明が明かすのはティアだけで、実行のサイクル数ではなく、パディングされた粗い区分としての実行長です。バンドルはすべてティア 14、認可証明はすべてティア 10 で、呼び出しは fullnode v0.5.6 以降ティア 14 が上限です。
maximum trace2²⁴ 行(zero-knowledge ビットありでは 2²⁵)拡大体の項が証明されているサイズです。これより長いトレースは、成り立たない健全性の主張のもとで証明されることなく、拒否されます。
public values体要素 35 個エントリの pc、ティア、出力ワード 8 個、8 ワードのダイジェスト 3 つ hc、H_IN、H_PUB、そして宣言されたガス上限です。プログラム、非公開入力、公開入力、そして処理量の上限を示す、最小の記述です。制約セット 7 までは 34 個でした。
proof size cap既定 2 MiB、chain 20 では 4 MiB(max_proof_bytes)2 MiB は、ハッシュテーブルを使わないデプロイ済みゲストが作るどの証明よりも大きく、keccak を含む証明(≈ 3.3 MB)よりは小さい値です。chain 20 のジェネシスは両方を受け入れるために 4,194,304 を設定しており、chain 17〜19 も同様でした。chain 13〜16 は 8 MiB を設定していました。上限は証明をデコードする前に検査されます。

これらの数値が示すもの:証明済みの健全性は 86 ビット、ethSTARK 予想のもとでは約 100 ビット(この場合、限界を決めるのはクエリではなく拡大体です)、量子敵対者に対しては約 85 ビットで、これは 256 ビットのトランスクリプトハッシュによって決まります。ホワイトペーパーは、2 つの健全性の数値をあえて並べて示しています。デプロイ済みの FRI-STARK はいずれも、最後の数ビットを予想に頼っており、デプロイの規模を見積もる読者は、どの仮定がどのビットを支えているかを知っておくべきだからです。

Proofs about proofs

バンドル証明は約 1.50 MB と重く、その隣に 1.37 MB の認可証明が付き、すべてのノードがそれらをすべて保存します。実装済みの対策は再帰です。つまり、「これらの証明を検証した」ことを主張する証明です。fullnode はそのために、より小さな 2 つ目のマシンである recursion VM を備えています。そのネイティブワードは Goldilocks 要素で、命令は検証者自身の演算(体算術、Poseidon2、Merkle ステップと FRI ステップ)です。その aggregate プログラムは N 個のバンドル証明の検証全体を再実行し、それらの公開値に対するダイジェストを、チェーン、アグリゲーター、ナンスに結び付けて公開します。その内部でバンドル証明 1 つを検証するのに、制約セット 6 では 1,968,619 行、制約セット 7 では 2,044,506 行かかりました。制約セット 8 では再測定していません。実装済みですが、chain 20 を含め、これまでのすべてのチェーンでジェネシスによって無効化されています。aggregate プログラムが検証するのはバンドル証明だけなので、認可を分割するチェーンでは認可証明が残ります。また、本番の証明に必要なメモリは想定を上回ります。制約セット 7 で測定したところ、133 GB 以上でした(Upcoming: block aggregation)。

What the scheme does not claim

  • 量子耐性のマージンは128ビットではなく、約85ビットです。 n ビットのハッシュに対する量子衝突探索のコストは、約 2ⁿ/³ です。ホワイトペーパーの設計では384ビットのトランスクリプトハッシュを想定していますが、稼働中のチェーンは256ビットの Poseidon2 ダイジェストを使っています。これを広げるには新しい制約セットが必要になり、ハードフォークを伴います。
  • 100ビットは予想であり、証明済みなのは86ビットです。 証明済みの値を100に引き上げるには97回のクエリが必要で、証明1件あたりのクエリデータは約5分の1増えます。
  • Poseidon2 の定数はシードから生成したものであり、公開されている標準のインスタンス化ではありません。これに対する攻撃は知られていません。標準の定数セットを採用すると、チェーン上のすべてのハッシュが変わります。
  • 一部の主張は、証明の中ではなく証明の外で検査されます。 TIER 公開値は、検証者(verifier)が証明の形状と照合しますが、回路では制約されていません(監査 ZL1)。また、バンドルゲストのテイント規律は、構造的な論証、弱めたゲストを用いたハーネス、ミューテーションファジングによって裏付けられており、機械的には検証されていません。
  • 証明のヘッダーは秘匿されません。 ティアと2つのハッシュテーブルの高さは平文で見えるため、呼び出しの証明から、その呼び出しが KECCAK と SHA256 のどちらを使ったかがわかります。両方のテーブルをパディングして含める証明者(prover)のオプションは存在しますが、設定しているウォレットはありません。このオプションを使うと、ティア10の証明は約1.3 MBから約3.6 MBになります。
  • POSEIDON2 システムコールは、可変長の入力に対して単射ではありません。 コミットメント、ナullifier、ルート、プログラムダイジェストのすべてがこれに依存しているため、現状のまま残しています。POSEIDON2_LEN を使う出荷済みのゲストはまだありません。
  • 証明の生成は低速で、送信者、または送信者が組む証明者(prover)が行います。 バンドル1件で、ノートPCのCPUでは約100秒、5.7 GBを要します。GPUカーネルは2026-09-28に初めてGPU(NVIDIA H100)上で動作し、CPUのリファレンスと一致しました。ウォレットは現在もCPUで証明を生成します。

さらに読む

  • ホワイトペーパーの「The Zero-Knowledge Scheme: How a Rand Proof Works」の節(randprotocol.tex)では、同じ7つの要素を、健全性の定理とともに順に説明しています。
  • 実装ペーパー(randprotocol_implementation.tex)では、命令セット、システムコール、バンドルゲスト、スポンジとダイジェスト、すべてのテーブルとバス、マシン構成、再帰VMを規定しています。
  • コード:fullnode の crates/randprotocol-zkvm/src/ — emulator.rs、tables/、machine.rs、gas.rs、guests.rs、hash.rs、poseidon2_constants.rs。

fullnode のコミット c0878c3f(チェーン 21)に対して 2026-10-12 に最終確認しました。また、fullnode `90e7716` (v0.6.7 on chain 18, `crates/randprotocol-zkvm`, constraint set 8), fullnode `c9c9bd3c` (v0.6.8 on chain 20: ZKV-4 in `crates/randprotocol-zkvm/src/executor.rs`, `binding_domain` in `crates/randprotocol-core/src/types/binding.rs`; no constraint change), fullnode `c0878c3f` (v0.8.0 on chain 21: `docs/zkvm.md` §8a, constraint set 9 — the `MODQ` syscall and the optional `modq` table; `docs/shielded.md` §2, the multisig auth guest's pinned shape), circuits `aeacf31`, `research/docs/02-tables-and-buses.md`, `research/docs/03-privacy.md` (the FRI profile, the degree ceiling, the table floor and the blinded terminals), whitepapers `8f4275b` (`randprotocol.tex` §“The Zero-Knowledge Scheme”, `randprotocol_implementation.tex` §§4.10–4.15)。 コードとこのページが食い違う場合は、コードが正です。お知らせください。