These four programs are each a complete application of a confidential call: a problem, an interface, a Rust guest, the host-side tooling, and a deploy and a call on the live testnet.
これら4つのプログラムは、いずれも機密コールの完全なアプリケーションです。問題設定、インターフェース、Rust のゲスト、ホスト側のツール、そしてライブ testnet へのデプロイとコール呼び出しを含みます。
Actually, let me produce the full translation properly.
These four programs are each a complete application of a confidential call: a problem, an interface, a Rust guest, the host-side tooling, and a deploy and a call on the live testnet.
これら4つのプログラムは、それぞれが機密コールの完全なアプリケーションです。問題、インターフェース、Rust のゲスト、ホスト側のツール、そしてライブ testnet へのデプロイとコールで構成されています。チュートリアルより長く、それぞれがマシンの異なる部分に依拠しています。Write a program in Rust or C は、これらすべてが用いるモデルのガイドです。read_input、パブリックセグメント、8つの出力スロットが初めての方は、先にお読みください。
ここに掲載するものはすべて、2026-09-30 に fullnode v0.6.7 を用いて chain 18 で最初に実行したもので、これらのページの出力はすべてその実行結果をそのまま貼り付けたものです。プログラムはチェーンの切り替えをまたいで引き継がれないため、2026-10-01 に4つのプログラムを変更せずに chain 19、続いて chain 20(fullnode v0.6.8)へ再度デプロイし、そのたびにコールを繰り返しました。プログラム ID はチェーンではなくコードとパブリックセグメントのハッシュであるため、どの ID も同じです。トランスクリプト中のトランザクションハッシュは chain 18 のものであり、下の表からは chain 20 のものにリンクしています。chain 20 では、プログラムはコールの間で状態を保持することもできます(RPL-2)。この4つはそれを使っていません。状態を扱うプログラムについては、Program state と example-programs repository を参照してください。
The four programs
| program | what a call proves | leans on | words | tier |
|---|---|---|---|---|
| An anonymous vote | 呼び出し元が、どのメンバーかを明かすことなく、公開されたリストのメンバーであり、まだ投票していないこと | Keccak-256、パブリックセグメント | 427 | 12 |
| A proof of liabilities | 顧客ごとの非公開の残高の合計が、公開された準備金を超えないこと。各顧客が確認できるルートに基づきます | SHA-256、プログラム内部で構築したツリー | 994 | 14 |
| An off-chain ledger | 一連の送金のバッチが、すべての送信者の承認を得たうえで何も新たに生成することなく、台帳をある状態から次の状態へ進めること | Poseidon2、コール間で引き継がれる状態 | 773 | 14 |
| A private credit decision | 公開されたモデルが、attester が保証した非公開の申請者データを承認または否認すること | 整数演算、パブリックセグメント内のモデル | 462 | 12 |
chain 20 では4つとも稼働しています。表の各コールは、そのプログラムのページに示した chain 18 のコールを、同じ入力で繰り返したものです。
| program | program id | deployed on chain 20 in | first call on chain 20 |
|---|---|---|---|
| anonymous vote | ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 | f6ae5528…0f82 | 3a304e7a…427b |
| proof of liabilities | fce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0 | a8bc281b…17d9 | 6116c15f…d62a |
| off-chain ledger | c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab | 72cc95f2…3d51 | 9c9998bb…66eb |
| private credit decision | 7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c | 413e6658…e42b | d3ceb306…124a |
エクスプローラーでは、すべてのプログラムとそのコールを randscan.org/programs で確認できます。testnet のチェーンは随時入れ替えられ、プログラムは引き継がれません。次の切り替え後にはこれらの ID は失われ、プログラムを再度デプロイする必要があります。
Setup
ツールチェーン。 ゲストは、circuits リポジトリの rand-guest でビルドします。コミットは、fullnode v0.6.7 と v0.6.8 の両方がベンダリングしているものです。
git clone https://github.com/randprotocol/zkp-circuits circuits
cd circuits
git checkout aeacf31 # branch feat/v067-circuits
rustup toolchain install 1.98.1
rustup target add riscv32im-unknown-none-elf --toolchain 1.98.1
(cd rand-guest && cargo +1.98.1 build --release)
alias rand-guest="$PWD/rand-guest/target/release/rand-guest"
このコミットが重要な理由は2つあります。hc はソースとツールチェーンの関数であるため、guest-sdk の別のコミットでゲストを再ビルドすると、異なる hc になることがあります。また、SDK のシグネチャが変わっています。aeacf31 では、keccak と sha256_compress は配列への参照を取りますが、それ以前のコミットでは生ポインタを取っていました。
ソース。 4つのゲスト、ホスト用スクリプト、デモデータは、まとめて1つのダウンロードになっています。rand-guest build はゲストのディレクトリから上方向にたどって guest-sdk を探すため、チェックアウトの内部に展開してください。
mkdir -p my-guests
curl -sSf https://randprotocol.org/examples/rand-example-programs.tar.gz | tar -xz --strip-components=1 -C my-guests
ls my-guests
# allowlist ledger ledger-host score solvency
ファイルは、https://randprotocol.org/examples/programs/ 配下で1つずつ閲覧することもできます。たとえば allowlist/src/main.rs です。
ウォレット。 デプロイとコールには、fullnode v0.6.8 の rand ウォレット(fullnode のチェックアウトをタグ v0.6.8 にして cargo build --release -p randprotocol-client を実行するか、v0.6.8 リリースに添付された rand)、鍵、そして RAND が必要です。古いウォレットでは chain 20 でトランザクションを送れません。chain 20 はすべてのプルーフと署名付きメッセージをそのジェネシスに結び付けますが、v0.6.7 のウォレットはそうしていないためです。
RPC。 パブリックエンドポイント https://rpc.randprotocol.org で、これらのページにあるすべての操作(プログラムとレシートの読み取り、ウォレットの同期、デプロイ、コール)が可能です。ウォレットの初回同期では、すべてのブロックヘッダーと、ノートを含むすべてのブロックを読み取ります。2026-09-30 時点の chain 18 の高さでは 645 リクエストで、このエンドポイント経由で約3分かかりました。エンドポイントは、1つのアドレスにつき1分あたり読み取り 1,200 回、送信 6 回まで許可しています。ご自身のノート(Running a node)には、このような制限はありません。
export RAND_RPC=https://rpc.randprotocol.org
export RAND_KEY=$HOME/wallet.key.json
chain 18 では、匿名投票のページで集計した票の1つ、fdbcc960…fc2b は、ラップトップでプルーフを生成し、パブリックエンドポイントだけを通じて送信したものです。chain 19 と chain 20 では、毎回13件のトランザクションすべて(4件のデプロイと9件のコール)がその方法で送られました。
What chain 18 changed for a program
コールはガスリミットを宣言し、その分を支払います。 制約セット 8 はすべての実行を計測します。通常の1サイクルは 1 ガスです。ハッシュのシステムコールは例外です。
| instruction | gas |
|---|---|
any RV32IM instruction, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC | 1 |
POSEIDON2 over n words | 3 + 3 × ⌈n / 4⌉ |
SHA256, one compression | 64 |
KECCAK, one permutation | 192 |
プルーフには新たに1つの公開値、宣言された上限が加わり、回路は実行のガスがそれ以下であることを強制します。正確なガスは非公開のままです。rand call はまずエミュレーターでプログラムを実行し、両方を表示します。
gas: 5636 (declaring 6144, tier 12)
既定では、ウォレットは正確なガスを 2^(tier − 2) の倍数に切り上げます。これは tier 12 では 1,024、tier 14 では 4,096 であるため、宣言された上限から実行についてわかることはほとんどありません。--gas-limit max は tier の上限を宣言し、tier 以外のことは何も明かしません。--gas-limit N は正確な値を宣言します。
手数料は、宣言された上限とバイト数に応じて決まります。
floor = 0.001 RAND + gas price × declared limit + byte price × ⌈bytes / 1024⌉
価格は、1ガスあたり 100 ユニット、1 KiB あたり 800 ユニット(1ユニットは 10⁻⁹ RAND)から始まり、ブロックごとに目標に向かって少しずつ動きます。rand_getLimits が現在の価格を返します。コールは価格が付けられてから2〜3ブロック後に確定するため、ウォレットは価格ステップ2回分の余裕、すなわち 1.125² 倍を上乗せします。
手数料の大半を決めるのは、ある1つの点です。chain 18 以降、コールのプルーフは自身のトランザクションに結び付けられるため、ウォレットはプルーフが生成される前に手数料を確定しなければならず、プルーフを実際のサイズではなくチェーンの上限である 4 MiB として見積もります。匿名投票の場合は (1,000,000 + 100 × 6,144 + 800 × 4,097) × 1.125² = 6,191,437 ユニット、0.0062 RAND となり、そのうちガスは約8分の1です。これらのページにある4つのコールの費用は、それぞれ 0.0057〜0.0075 RAND でした。--fee で既定値を上書きできます。
すべてのバンドルは2つのプルーフです。 chain 18 は分割された承認を採用しています。手数料を支払うバンドルには、tier 14 のバンドルプルーフに加えて、小さな tier 10 の承認プルーフが含まれます。ウォレットは両方を生成し、それぞれについて1行ずつ表示します。したがって1回のコールは、コール、承認、バンドルの3つのプルーフになります。Apple Silicon のラップトップでは、ここにある4つのコールはそれぞれ3分から5分半かかり、メモリのピークは 6.1〜6.6 GB でした。そのうちバンドルのプルーフは毎回約2分です。tier 12 のコールプルーフは30秒、tier 14 のものは2分が加わります。
コールが収まるべき上限。
| limit | chains 18 to 20 |
|---|---|
| tier | at most 14: 16,383 cycles, digest rows included |
| Poseidon2 permutations | 2,048 at tier 14, the program, input and public digests included |
| Keccak permutations | 128 |
| SHA-256 compressions | 128 |
| call proof | 4,194,304 bytes (4 MiB) |
| program | 8,180 words with no public segment, a few fewer with one: chain 20, like chains 18 and 19, refuses a deploy that no tier-14 call could hold (ProgramUncallable), whatever max_program_words (65,535) allows |
| public segment | 32,768 words, fixed at deploy |
| private input with a sealed transcript | 16,071 words |
rand-guest run --tier 14 は、デプロイ前に最初の2つを確認します。ハッシュテーブルはプルーフを大きくします。ハッシュテーブルのない台帳の tier 14 のプルーフは 1.50 MB です。SHA-256 テーブルにより債務証明のプルーフは 1.91 MB に、Keccak テーブルにより投票のプルーフは 3.35 MB になり、4 MiB の上限の大部分を占めます。
Conventions the four share
- スロット0はステータスであり、0は有効な値ではありません。 失敗を含むすべての結果に1以上の番号が付き、失敗はクリーンに停止するため、証明できます。すべてゼロのレシートが結果と取り違えられることはありません。
- スロット7には手を加えません。 パニックを起こした Rust ゲストは、そこに
0xdeadbeef(3,735,928,559)を書き込みます。読み手は最初にスロット7を確認します。 - スロット2から6は160ビットの値を保持します。 ナリファイア、ルート、リンク、コミットメントなどです。256ビットのハッシュ全体は8ワードに及び、ステータス用の余地が残りません。5ワードに切り詰めると衝突耐性は80ビット残り、各ページはそれが何を守っているかを説明しています。
- バイト順はハッシュに従います。 Keccak の値はリトルエンディアンのワード、SHA-256 の値はビッグエンディアンのワードです。各システムコールがブロックをそのように読み取るためです。ホストスクリプトとゲストの間でこれが食い違うと、ダイジェストは決して一致しません。
- インスタンスごとに変わるものは公開セグメントに入り、呼び出しごとのものは非公開入力です。 投票のルート、準備金の数値、モデルの重みはデプロイ時に固定され、プログラム id に束縛されます。呼び出しそのものは公開ワードを持ちません。
- 入力ワードはコマンドラインでは10進数です。 各ページでは、
$(sed 's/[0-9][0-9]*/--input &/g' witness.txt)を使ってウィットネスファイルをフラグに変換します。
自分で呼び出してみる
ソースを展開し、ツールチェーンをビルドし、資金のある v0.6.8 のウォレットを用意すれば、デモメンバーの一人としての投票は4つのコマンドで行えます。
cd my-guests/allowlist
python3 allowlist.py witness 10 1 demo-secrets.txt 6 1 > witness.txt # member 6, choice 1
rand-guest build . --max-words 65535 # only needed to run it in the emulator first
rand call ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 \
$(sed 's/[0-9][0-9]*/--input &/g' witness.txt)
呼び出しにイメージは必要ありません。rand call はノードからプログラムのコードと公開セグメントを取得し、それらのハッシュが id と一致することを確認したうえで、それらに対して証明を行います。
結果の読み取りにウォレットもノードも不要です。どのレシートも、公開エンドポイントへの1回のリクエストで取得できます。
$ curl -s https://rpc.randprotocol.org -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"rand_getReceipt","params":["8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"]}'
{"id":1,"jsonrpc":"2.0","result":{"h_in":"ea71e7701e62d49cac37b239bef132178f481835027d62b34da45fd6cf2667c6","h_pub":"2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510","height":98335,"index":0,"outputs":[1,2,392974773,873526743,1921224601,3950868247,2248722827,0],"program":"ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970","tier":12,"tx":"8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"}}
エクスプローラーの API では、プログラムの呼び出しを1回のリクエストで取得できます。https://randscan.org/api/v1/programs/<program id> は call_count と recent_calls を返します(Explorer API)。
いずれも行わないこと
Rand 上のプログラムは計算についての証明であり、チェーンはその出力のいずれに対しても動作しません。これらのプログラムはいずれも、RAND やトークンを移動せず、残高を保持せず、以前の呼び出しを記憶しません。投票は、レシートを読む人が集計します。負債は、独自の証拠を必要とする準備金と照合されます。台帳の残高は、オフチェーンのツリーにある数値です。与信の判断は、貸し手が尊重するかどうかを選べる1ビットです。各ページの末尾には、その証明が利用者に委ねている事柄の一覧があります。