Rand Protocol
ドキュメント
サンプルプログラム

chain 20 上の4つのプログラム

testnet にデプロイされた4つの完結した機密プログラム(匿名投票、負債の証明、オフチェーン台帳、非公開の与信判断)について、プログラム id、共通のセットアップ、chain 18 でプログラム作成者にとって何が変わったか、そして自分で呼び出す方法を説明します。

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

programwhat a call provesleans onwordstier
An anonymous vote呼び出し元が、どのメンバーかを明かすことなく、公開されたリストのメンバーであり、まだ投票していないことKeccak-256、パブリックセグメント42712
A proof of liabilities顧客ごとの非公開の残高の合計が、公開された準備金を超えないこと。各顧客が確認できるルートに基づきますSHA-256、プログラム内部で構築したツリー99414
An off-chain ledger一連の送金のバッチが、すべての送信者の承認を得たうえで何も新たに生成することなく、台帳をある状態から次の状態へ進めることPoseidon2、コール間で引き継がれる状態77314
A private credit decision公開されたモデルが、attester が保証した非公開の申請者データを承認または否認すること整数演算、パブリックセグメント内のモデル46212

chain 20 では4つとも稼働しています。表の各コールは、そのプログラムのページに示した chain 18 のコールを、同じ入力で繰り返したものです。

programprogram iddeployed on chain 20 infirst call on chain 20
anonymous votead2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970f6ae5528…0f823a304e7a…427b
proof of liabilitiesfce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0a8bc281b…17d96116c15f…d62a
off-chain ledgerc4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab72cc95f2…3d519c9998bb…66eb
private credit decision7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c413e6658…e42bd3ceb306…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 ガスです。ハッシュのシステムコールは例外です。

instructiongas
any RV32IM instruction, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC1
POSEIDON2 over n words3 + 3 × ⌈n / 4⌉
SHA256, one compression64
KECCAK, one permutation192

プルーフには新たに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分が加わります。

コールが収まるべき上限。

limitchains 18 to 20
tierat most 14: 16,383 cycles, digest rows included
Poseidon2 permutations2,048 at tier 14, the program, input and public digests included
Keccak permutations128
SHA-256 compressions128
call proof4,194,304 bytes (4 MiB)
program8,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 segment32,768 words, fixed at deploy
private input with a sealed transcript16,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ビットです。各ページの末尾には、その証明が利用者に委ねている事柄の一覧があります。

fullnode のコミット 48dc04b(チェーン 18)に対して 2026-09-30 に最終確認しました。また、circuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest`), the commit fullnode v0.6.7 vendors; `docs/fees.md`, `docs/zkvm.md`, `docs/cli.md` and `crates/randprotocol-client/src/main.rs` at fullnode `48dc04b`; the four deploys and the four calls were made on chain 18 on 2026-09-30, and every receipt was read back from `https://rpc.randprotocol.org`。 コードとこのページが食い違う場合は、コードが正です。お知らせください。