Rand Protocol
文檔
範例程式

chain 20 上的四個程式

四個已部署於 testnet 的完整保密程式——匿名投票、負債證明、鏈下賬本及私密信貸決策——附程式 id、它們共用的設置、chain 18 為程式作者帶來的改變,以及如何自行調用這些程式。

這四個程式各是一個機密調用的完整應用:一道問題、一個介面、一個 Rust guest、主機端工具,以及在線上 testnet 的一次部署與一次調用。它們比教程更長,而且各自倚重機器的不同部分。以 Rust 或 C 編寫程式是它們共用模型的指南。若 read_input、公開段或八個輸出槽對你而言尚屬陌生,請先閱讀該文。

此處一切均先在 chain 18 上以 fullnode v0.6.7 運行,時間為 2026-09-30,頁面上的每一項輸出均由那些運行結果直接貼上。程式不會跨越 chain 切換而延續,因此在 2026-10-01,這四個程式原封不動地重新部署到 chain 19,其後再部署到 chain 20(fullnode v0.6.8),每次均重複了調用。程式 id 是程式碼與公開段的哈希,而非 chain 的哈希,因此所有 id 均相同;記錄中的交易哈希屬於 chain 18,下表連結的則是 chain 20 的交易。在 chain 20 上,程式亦可在多次調用之間保存狀態(RPL-2);這四個程式並不使用,而程式狀態與 example-programs 倉庫涵蓋了使用狀態的程式。

四個程式

程式一次調用所證明的內容倚重字數層級
匿名投票調用者是已公布名單中的成員而不透露是哪一位,且此前未曾投票Keccak-256、公開段42712
負債證明客戶的私密餘額合計不超過已公布的儲備,且各客戶可核對所屬的根SHA-256、在程式內構建的樹99414
鏈下賬本一批轉賬使賬本由一個狀態進入下一個狀態,每名發送者均已獲授權,且沒有憑空產生任何價值Poseidon2、在多次調用之間攜帶的狀態77314
私密信貸決定一個已公布的模型批准或拒絕由認證方擔保的私密申請人數據整數運算、公開段中的模型46212

四個程式在 chain 20 上均在線運行,直至其於 2026-10-12 退役。Chain 21(fullnode v0.8.0)一如每次切換,起始時並無任何程式,這四個程式尚未在該處重新部署;重新部署時,下列 id 不會改變,因為 id 是程式碼與公開段的哈希,而非 chain 的哈希。表中每次調用均以相同輸入重複該程式頁面所示的 chain 18 調用:

程式程式 id於 chain 20 部署的交易於 chain 20 的首次調用
匿名投票ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970f6ae5528…0f823a304e7a…427b
負債證明fce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0a8bc281b…17d96116c15f…d62a
鏈下賬本c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab72cc95f2…3d519c9998bb…66eb
私密信貸決定7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c413e6658…e42bd3ceb306…124a

瀏覽器 randscan.org/programs 一頁列出每個程式及其調用,位於 randscan.org/programs。testnet chain 會不時被替換,程式亦不會延續:下次切換之後,這些 id 將不復存在,程式需要重新部署。

設置

**工具鏈。**guest 以電路倉庫中的 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"

該提交之所以重要,有兩個原因。hc 是源碼與工具鏈的函數,因此在 guest-sdk 的另一個提交上重新構建的 guest 可能得出不同的 hc。此外,SDK 的簽名亦有變動:在 aeacf31,keccak 與 sha256_compress 接受數組引用,而較舊的提交接受的是原始指針。

**源碼。**四個 guest、其主機腳本與示範數據合為一個下載包。請在 checkout 之內解壓,因為 rand-guest build 會從 guest 所在目錄向上查找 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/ 下逐個瀏覽,例如 allowlist/src/main.rs。

**錢包。**部署與調用需要 fullnode v0.6.8 的 rand 錢包(在 fullnode checkout 中於標籤 v0.6.8 下執行 cargo build --release -p randprotocol-client,或使用 v0.6.8 發佈版本所附的 rand)、一條密鑰,以及 RAND。在 chain 21 上,錢包為 fullnode v0.8.0(標籤 v0.8.0)的 rand:約束集 9 改變了每一個 verifier 密鑰,因此 v0.6.8 與 v0.7.x 的錢包均無法在該處部署或調用。較舊的錢包同樣無法在 chain 20 上交易:chain 20 將每個證明與每條已簽名訊息綁定至其創世區塊,而 v0.6.7 錢包並不如此。

**RPC。**公共端點 https://rpc.randprotocol.org 足以應付這些頁面上的一切:讀取程式與回執、同步錢包、部署與調用。錢包首次同步須讀取每個區塊頭與每個含 note 的區塊,在 2026-09-30 chain 18 的高度下為 645 個請求,經該端點約需三分鐘。該端點容許每個地址每分鐘 1,200 次讀取與 6 次發送。你自己的節點(運行節點)則沒有此類限制。

export RAND_RPC=https://rpc.randprotocol.org
export RAND_KEY=$HOME/wallet.key.json

在 chain 18 上,匿名投票頁面所計的其中一票 fdbcc960…fc2b,在一部手提電腦上生成證明,並僅經公共端點發送。在 chain 19 與 chain 20 上,每次的全部十三筆交易(四次部署與九次調用)均以此方式完成。

chain 18 為程式帶來的改變

**調用須聲明 gas 上限,並為此付費。**約束集 8 對每次運行計量。一般的週期為 1 gas。哈希系統調用則是例外:

指令gas
任何 RV32IM 指令、HALT、WRITE_OUTPUT、READ_INPUT、READ_PUBLIC1
對 n 個字的 POSEIDON23 + 3 × ⌈n / 4⌉
SHA256,一次壓縮64
KECCAK,一次置換192

證明帶有一個新的公開值,即聲明上限,而電路強制要求該次運行的 gas 不高於它。確切的 gas 保持私密。rand call 會先在模擬器中運行程式,並印出兩者:

gas: 5636 (declaring 6144, tier 12)

錢包預設會將確切的 gas 向上取整至 2^(tier − 2) 的倍數,即層級 12 為 1,024、層級 14 為 4,096,因此聲明上限對該次運行幾乎沒有透露甚麼。--gas-limit max 聲明該層級的上限,除層級之外不透露任何信息。--gas-limit N 則聲明一個確切數字。

費用取決於聲明上限與位元組數。

floor = 0.001 RAND  +  gas price × declared limit  +  byte price × ⌈bytes / 1024⌉

價格起初為每 gas 100 單位、每 KiB 800 單位(一單位為 10⁻⁹ RAND),並每個區塊向目標略作移動。rand_getLimits 提供當前價格。錢包會加上兩個價格步幅的餘裕,即 1.125² 倍,因為調用在定價後兩三個區塊才會落地。

有一個細節決定了大部分費用。自 chain 18 起,調用的證明綁定至其自身交易,因此錢包必須在證明存在之前確定費用,並按鏈上 4 MiB 的上限而非其實際大小為證明定價。對匿名投票而言,即 (1,000,000 + 100 × 6,144 + 800 × 4,097) × 1.125² = 6,191,437 單位,即 0.0062 RAND,其中 gas 約佔八分之一。這些頁面上的四次調用,每次費用介乎 0.0057 至 0.0075 RAND。--fee 可覆蓋預設值。

**每個 bundle 含兩個證明。**Chain 18 採用拆分授權:支付費用的 bundle 除層級 14 的 bundle 證明外,還附帶一個小型的層級 10 授權證明。錢包會生成兩者,並各印出一行。因此一次調用共有三個證明:調用、授權與 bundle。在 Apple Silicon 手提電腦上,此處的四次調用每次耗時三至五分半鐘,記憶體峰值為 6.1 至 6.6 GB。其中 bundle 證明每次約佔兩分鐘。層級 12 的調用證明增加半分鐘,層級 14 的則增加兩分鐘。

調用必須符合的上限。

限制chain 18 至 20
層級至多 14:16,383 個週期,包括摘要行
Poseidon2 置換層級 14 為 2,048,包括程式、輸入與公開摘要
Keccak 置換128
SHA-256 壓縮128
調用證明4,194,304 位元組(4 MiB)
程式無公開段時為 8,180 個字,有公開段時則少數個字:chain 21 與 chain 18 至 20 一樣,拒絕任何層級 14 調用均無法容納的部署(ProgramUncallable),不論 max_program_words(65,535)允許多少
公開段32,768 個字,於部署時固定
附封存記錄的私密輸入16,071 個字

rand-guest run --tier 14 會在部署前檢查頭兩項。哈希表會令證明變大。賬本的層級 14 證明沒有哈希表,為 1.50 MB。SHA-256 表使負債證明增至 1.91 MB,而 Keccak 表使投票的證明增至 3.35 MB,已佔 4 MiB 上限的大部分。

四個程式共通的慣例

  • 槽位 0 是狀態,而 0 絕不是有效的狀態值。 每一種結果(包括失敗)都有一個由 1 起算的編號,而失敗會乾淨地停機,以便可以被證明。全為零的收據不會被誤認為一個結果。
  • 槽位 7 不作使用。 發生 panic 的 Rust guest 會在該處寫入 0xdeadbeef(3,735,928,559)。讀取者應先檢查槽位 7。
  • 槽位 2 至 6 承載一個 160 位元的數值:nullifier、根、連結或承諾。完整的 256 位元雜湊值佔八個字,會令狀態無處容身。截短為五個字後,仍保留 80 位元的抗碰撞強度,而各頁面會說明這一強度所保護的是甚麼。
  • 位元組順序依從雜湊。 Keccak 的數值是小端序的字,SHA-256 的數值是大端序的字,因為各個系統呼叫正是這樣讀取其區塊。主機腳本與 guest 若在這一點上不一致,產生的摘要便永遠不會吻合。
  • 隨實例而異的內容置於公開段;隨每次呼叫而異的內容為私有輸入。 投票的根、儲備數字和模型的權重在部署時已固定,並綁定於程式 id。呼叫本身不附帶任何公開字。
  • 輸入字在命令列上以十進位表示。 各頁面以 $(sed 's/[0-9][0-9]*/--input &/g' witness.txt) 把見證檔案轉為旗標。

親自呼叫一次

在源碼已解壓、工具鏈已建置,並備有已注資的錢包之後(v0.6.8 於 chain 20,這些運行即在其上進行;v0.8.0 於 chain 21),以示範成員之一的身分投票,只需四條命令:

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 相符,然後在其上進行證明。

讀取結果不需要錢包,也不需要節點。任何收據都只需向公開端點發出一次請求:

$ 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 可一次請求取得某個程式的全部呼叫:https://randscan.org/api/v1/programs/<program id> 會返回 call_count 和 recent_calls(Explorer API)。

它們都不做的事

Rand 上的程式是對一項計算的證明,而鏈本身不會依據其任何輸出採取行動。這些程式沒有一個會轉移 RAND 或代幣、持有餘額,或記住先前的呼叫。投票由讀取收據的人點算。負債要與儲備比對,而儲備需要它們自己的憑證。帳本的餘額是鏈下樹中的數字。信貸決定是貸款人自行選擇是否遵從的一個位元。每一頁的結尾都列出其證明留給使用者自行處理的事項。

最後一次對照 fullnode 提交 48dc04b(chain 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`。 如代碼與本頁內容有出入,以代碼為準;請告知我們。