Rand Protocol
दस्तावेज़ीकरण
RISC-V प्रोग्राम

Rust प्रोग्राम डिप्लॉय करें

no_std Rust crate से लेकर डिप्लॉय किए गए गोपनीय प्रोग्राम तक, चरण दर चरण: पिन किया गया टूलचेन इंस्टॉल करें, guest लिखें, rand-guest से उसे बिल्ड और जाँचें, एमुलेटर पर चलाएँ, उसे डिप्लॉय करें और कॉल करें।

यह पृष्ठ एक छोटे Rust प्रोग्राम को स्रोत से लेकर Rand पर डिप्लॉय किए गए प्रोग्राम तक ले जाता है। नीचे दिया गया हर rand-guest कमांड चलाया गया था, और उसका आउटपुट ज्यों-का-त्यों चिपकाया गया है, सारांश नहीं। ये रन 2026-09-19 को circuits कमिट 7ef3220 पर किए गए थे। testnet अब chain 20 है, जिसका टूलचेन chain 18 और 19 की तरह aeacf31 पर बना है (fullnode v0.6.8 वही कमिट पिन करता है जो v0.6.7 करता है), और आउटपुट वहाँ दोबारा नहीं चलाए गए हैं। RISC-V ISA मशीन, सिस्कॉल और फ़ाइल फ़ॉर्मैट की व्याख्या करता है।

1. टूलचेन इंस्टॉल करें

hc सटीक कंपाइलर पर निर्भर करता है, इसलिए टूलचेन पिन किया गया है।

टूलसंस्करणक्यों
Rust1.98.1गेस्ट और स्वयं rand-guest को कंपाइल करता है
riscv32im-unknown-none-elf टारगेट1.98.1 के लिएगेस्ट का टारगेट
एक circuits चेकआउटchain 18 से 20 के लिए aeacf31 पर randprotocol/zkp-circuitsइसमें rand-guest, guest-sdk और लिंकर स्क्रिप्ट हैं
git clone https://github.com/randprotocol/zkp-circuits circuits
cd circuits
git checkout aeacf31                      # branch feat/v067-circuits, what fullnode v0.6.7 and v0.6.8 pin
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"

rand-guest के पाँच सबकमांड हैं: build, check, pack, run और info।

कमिट इसलिए मायने रखता है क्योंकि hc स्रोत और टूलचेन का फ़ंक्शन है: guest-sdk के किसी दूसरे कमिट पर दोबारा बनाया गया गेस्ट भिन्न hc पा सकता है। 7ef3220 और aeacf31 के बीच rand-guest डायरेक्टरी नहीं बदली, जबकि guest-sdk/src/lib.rs बदली (चरण 2)।

2. गेस्ट लिखें

गेस्ट एक no_std, no_main बाइनरी crate है जो guest-sdk पर निर्भर करता है। इसे circuits चेकआउट के भीतर होना चाहिए: rand-guest build गेस्ट की डायरेक्टरी के ऊपर guest-sdk/ खोजकर चेकआउट का पता लगाता है, और चेकआउट से बाहर के गेस्ट को अस्वीकार कर देता है।

यह गेस्ट एक सीमा, एक गिनती और उतने ही निजी बैलेंस पढ़ता है। यदि बैलेंस सीमा को पूरा करते हैं, तो यह 1 और अधिशेष प्रकाशित करता है। यह बैलेंस कभी प्रकाशित नहीं करता।

circuits/
  guest-sdk/
  rand-guest/
  my-guests/
    threshold/
      Cargo.toml
      src/main.rs

my-guests/threshold/Cargo.toml:

[package]
name = "threshold"
version = "0.1.0"
edition = "2021"

[dependencies]
guest-sdk = { path = "../../guest-sdk" }

[profile.release]
opt-level = 3
lto = true
panic = "abort"
codegen-units = 1

[workspace]

खाली [workspace] crate को किसी भी पैरेंट workspace से बाहर रखता है, जैसा कि कमिट किया गया fib गेस्ट करता है।

my-guests/threshold/src/main.rs:

#![no_std]
#![no_main]

use guest_sdk::{halt, read_input, write_output};

/// Input 0 is a threshold, input 1 a count n, inputs 2..2+n are private balances.
/// Output 0 is 1 if the balances sum to at least the threshold, else 0.
/// Output 1 is the surplus when they do. The balances themselves are never published.
#[no_mangle]
pub extern "C" fn main() -> ! {
    let threshold = read_input(0);
    let n = read_input(1);
    let mut sum: u32 = 0;
    for i in 0..n {
        sum = sum.wrapping_add(read_input(2 + i));
    }
    if sum >= threshold {
        write_output(0, 1);
        write_output(1, sum - threshold);
    }
    halt();
}

guest-sdk, read_input, read_public, write_output, poseidon2, keccak, keccak256, sha256_compress, sha256 और halt एक्सपोर्ट करता है, और _start प्रदान करता है, जो स्टैक पॉइंटर सेट करता है और main को कॉल करता है। इसमें न कोई एलोकेटर है और न फ़्लोटिंग पॉइंट।

aeacf31 पर इनमें से तीन सिग्नेचर 7ef3220 से भिन्न हैं, और एक नया एक्सपोर्ट है:

फ़ंक्शन7ef3220 परaeacf31 पर
keccakfn keccak(ptr: *mut u32)fn keccak(state: &mut [u32; 50])
sha256_compressfn sha256_compress(ptr: *mut u32)fn sha256_compress(buf: &mut [u32; 24])
poseidon2fn poseidon2(ptr: *mut u32, n: usize)unsafe fn poseidon2(ptr: *mut u32, n: usize)
poseidon2_lenमौजूद नहींfn poseidon2_len(buf: &mut [u32], n: usize) -> [u32; 8], सिस्कॉल 7

poseidon2_len संदेश की लंबाई को स्पंज में बाँधता है और यही सुरक्षित कॉल है। aeacf31 पर rand-guest check अब भी केवल सिस्कॉल 0 से 6 सूचीबद्ध करता है, इसलिए जो गेस्ट इसे कॉल करता है, वह ऐसी इमेज बनाता है जिसे build ecall with a7 = 7, which is no syscall the machine implements के साथ अस्वीकार कर देता है (zkp-circuits issue #1)। जब तक यह ठीक नहीं होता, गेस्ट निश्चित लंबाई वाले, टैग किए गए संदेशों पर unsafe { poseidon2(ptr, n) } से हैश करता है। इस पृष्ठ का गेस्ट इन चारों में से किसी का उपयोग नहीं करता।

गेस्ट डायरेक्टरी के नियम:

  • कोई .cargo/config या .cargo/config.toml नहीं, न इसमें, न किसी पैरेंट में, न $CARGO_HOME में। build अस्वीकार करता है और हर फ़ाइल का नाम बताता है, क्योंकि cargo उसे बिल्ड में मिला देगा।
  • अपने कोई कंपाइलर फ़्लैग नहीं। build सभी फ़्लैग स्वयं सेट करता है: लिंकर स्क्रिप्ट, -unaligned-scalar-mem, और एक पाथ रीमैप जो इमेज को इस बात से स्वतंत्र बनाता है कि चेकआउट कहाँ स्थित है।
  • अधिकतम एक .ld फ़ाइल। build गेस्ट की डायरेक्टरी में मौजूद एकमात्र .ld का उपयोग करता है, या कोई न हो तो guest-sdk/guest.ld का। एक से अधिक होने पर उसे --ld से निर्दिष्ट करें।

3. बिल्ड और जाँच

$ rand-guest build my-guests/threshold --out threshold.bin
   Compiling guest-sdk v0.1.0 (…/circuits/guest-sdk)
warning: unstable feature specified for `-Ctarget-feature`: `unaligned-scalar-mem`
  |
  = note: this feature is not stably supported; its behavior can change in the future

warning: `guest-sdk` (lib) generated 1 warning
   Compiling threshold v0.1.0 (…/circuits/my-guests/threshold)
warning: `threshold` (bin "threshold") generated 1 warning (1 duplicate)
    Finished `release` profile [optimized] target(s) in 0.15s
40 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK
wrote threshold.bin and its .sha256 (40 words, hc a491df99f289d81e2b6e09849a850b14e36e2bd6edc5f5a6de46c3d67c703665, program id 1b129cbce89df252150ab3d387a927beb1650a40e6cb0b04e86cdc203ca1b853)

unaligned-scalar-mem चेतावनी अपेक्षित है: टूलचेन जानबूझकर असंरेखित एक्सेस बंद करता है।

build, cargo +1.98.1 build --release --target riscv32im-unknown-none-elf से कंपाइल करता है, परिणाम पर check चलाता है, और इमेज कंटेनर तथा एक .sha256 पिन लिखता है। यदि check प्रोग्राम को अस्वीकार करता है, तो भी build इमेज लिखता है और शून्येतर कोड के साथ बाहर निकलता है।

फ़्लैगडिफ़ॉल्टअर्थ
--out <path><dir>/image.binइमेज कहाँ जाती है
--max-words <n>4096वह सीमा जिसके विरुद्ध check मापता है; लक्ष्य चेन की सीमा दें (chain 20 पर 65,535)
--ld <path>डायरेक्टरी में मौजूद एकमात्र .ld, अन्यथा guest-sdk/guest.ldलिंकर स्क्रिप्ट
--lang rust|crustकंपाइलर

4. इसे एमुलेटर पर चलाएँ

निजी इनपुट --input से दें, प्रत्येक में एक वर्ड। --tier t यह भी बताता है कि रन टियर t में समाता है या नहीं।

$ rand-guest run threshold.bin --input 1000 --input 4 --input 400 --input 250 --input 300 --input 75 --tier 10
out[0] = 1
out[1] = 25
out[2] = 0
out[3] = 0
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 0
cycles 52
tier 10
tier 10: fits (cycles 66 of 1023, Poseidon2 permutations 14 of 128)

बैलेंस का योग 1,025 है, इसलिए प्रोग्राम 1 और 25 का अधिशेष प्रकाशित करता है। बैलेंस 400 और 250 होने पर योग कम पड़ता है, और हर आउटपुट शून्य होता है। ट्रैप होने पर trap: <error> प्रिंट होता है और प्रोग्राम कोड 2 के साथ बाहर निकलता है।

5. इमेज का निरीक्षण करें

$ rand-guest info threshold.bin
form: image container
text 40 words at 0x1000; data 0 words (0 non-zero) at 0x0; prologue 0 words; program 40 words from base_pc 0x1000
hc a491df99f289d81e2b6e09849a850b14e36e2bd6edc5f5a6de46c3d67c703665
program id 1b129cbce89df252150ab3d387a927beb1650a40e6cb0b04e86cdc203ca1b853
40 words against a cap of 4096: fits

hc वह है जिसके विरुद्ध किसी कॉल के प्रमाण का सत्यापन होता है। प्रोग्राम id वह है जिसके अंतर्गत आप डिप्लॉय और कॉल करते हैं।

6. डिप्लॉय करें

fullnode v0.3 या उसके बाद का rand वॉलेट उपयोग करें; पुराने वॉलेट इमेज कंटेनर नहीं पढ़ते। chain 14 से हर बंडल प्रमाण का सत्यापन उसके ट्रांज़ैक्शन के बाइंडिंग के विरुद्ध होता है, इसलिए chain-14 बिल्ड (b3c594c) से पुराना वॉलेट ऐसा बंडल प्रमाणित करता है जिसे चेन अस्वीकार कर देती है। लाइव चेन chain 20 पर fullnode v0.6.8 या उसके बाद का rand उपयोग करें (फ़्लीट v0.6.8 चलाता है, और Linux पर बना rand उसके GitHub रिलीज़ के साथ संलग्न है)। v0.6.7 वॉलेट chain 20 पर भेज नहीं सकता: chain 20 का जेनेसिस binding_domain: 1 सेट करता है, इसलिए हर प्रमाण बाइंडिंग और हर हस्ताक्षरित संदेश में जेनेसिस हैश शामिल होता है, जबकि v0.6.7 वॉलेट केवल चेन id को बाँधता है। वॉलेट को सिंक करने, डिप्लॉय करने और कॉल करने के लिए सार्वजनिक एंडपॉइंट https://rpc.randprotocol.org पर्याप्त है (सेटअप)।

rand fee deploy 40               # the floor for a 40-word program
rand program deploy threshold.bin

program deploy प्रोग्राम id, वर्ड संख्या और hc प्रिंट करता है। फिर यह rand_estimateFee से नोड से डिप्लॉय शुल्क पूछता है, जो चेन की सीमा से बड़े प्रोग्राम को किसी भी प्रमाणन से पहले अस्वीकार कर देता है। इसके बाद यह शुल्क चुकाने वाला एक शून्य-मूल्य स्व-हस्तांतरण बंडल प्रमाणित करता है, उसे सबमिट करता है, और कमिट की प्रतीक्षा करता है। यह बंडल वही 4-इनपुट, 4-आउटपुट छिपी-परिसंपत्ति बंडल है जो हर ट्रांज़ैक्शन में होता है। chain 17 से इसमें दो प्रमाण होते हैं, और वॉलेट प्रत्येक के लिए एक पंक्ति प्रिंट करता है: एक टियर-10 व्यय प्राधिकरण (कुछ सेकंड, लगभग 1.37 MB) और टियर-14 बंडल (लैपटॉप पर लगभग दो मिनट, लगभग 1.5 MB)। chain 15 पर यह लगभग 100 s और लगभग 1.42 MB का एक टियर-14 प्रमाण था। एक अनाम मतदान chain 18 पर एक डिप्लॉय को वैसा ही दिखाता है जैसा वॉलेट ने प्रिंट किया था।

डिप्लॉय शुल्क 0.001 RAND + 0.0001 RAND × words है, chain 20 पर भी पहले की तरह। गैस कॉल का मूल्य तय करती है, डिप्लॉय का नहीं:

प्रोग्रामवर्डडिप्लॉय शुल्क
threshold400.005 RAND
डिफ़ॉल्ट सीमा पर एक प्रोग्राम (chain 12)4,0960.4106 RAND
बिना सार्वजनिक सेगमेंट वाला सबसे बड़ा प्रोग्राम जिसे chain 20 डिप्लॉय करती है8,1800.819 RAND
65,535-वर्ड सीमा पर एक प्रोग्राम (chain 13 से 15)65,5356.5545 RAND

threshold 4,096 वर्ड से छोटा है, इसलिए यह डिफ़ॉल्ट सीमा और chain 20 की 65,535 की सीमा, दोनों में समाता है। 4,096 वर्ड से बड़े प्रोग्राम को ऐसी चेन चाहिए जिसका जेनेसिस max_program_words को कम से कम उतना ऊँचा सेट करता हो, जैसे chain 20 (RISC-V ISA)। chain 20 पर, chain 19 की तरह, एक दूसरी, निचली सीमा भी है: यह ऐसे प्रोग्राम को डिप्लॉय करने से मना करती है जिसे कोई टियर-14 कॉल समाहित न कर सके (ProgramUncallable), यानी बिना सार्वजनिक सेगमेंट के 8,180 वर्ड से अधिक, और सार्वजनिक सेगमेंट होने पर इससे कम। rand program deploy --input <word> … (या बिना इनपुट वाली कॉल के लिए --check-call) आपकी इच्छित कॉल को पहले एमुलेटर में चलाता है और तब तक डिप्लॉय करने से मना करता है जब तक वह टियर 14 या उससे नीचे न आए।

रिकॉर्ड जाँचें:

rand program show 1b129cbce89df252150ab3d387a927beb1650a40e6cb0b04e86cdc203ca1b853

इसका code_hash वही hc है जो rand-guest ने प्रिंट किया था, बस अलग ढंग से लिखा गया है। rand-guest डाइजेस्ट के आठों वर्ड में से प्रत्येक को big-endian में प्रिंट करता है, जबकि वॉलेट, rand program show और rand_getProgram प्रत्येक वर्ड के बाइट little-endian में प्रिंट करते हैं। इसलिए a491df99…3665, 99df91a4…707c के रूप में दिखता है: आठ हेक्स अंकों का हर समूह बाइट-उलटा होता है। प्रोग्राम id दोनों में एक ही तरह लिखा जाता है।

7. इसे कॉल करें

rand call 1b129cbce89df252150ab3d387a927beb1650a40e6cb0b04e86cdc203ca1b853 \
    --input 1000 --input 4 --input 400 --input 250 --input 300 --input 75

वॉलेट कोड प्राप्त करता है, कॉल को एमुलेटर में चलाता है, और कुछ भी प्रमाणित करने से पहले gas: <exact> (declaring <limit>, tier <t>) तथा शुल्क प्रिंट करता है। फिर यह इनपुट ट्रांसक्रिप्ट को सील करता है, रन को स्थानीय रूप से प्रमाणित करता है, एक बंडल के माध्यम से शुल्क चुकाता है, और रसीद प्रिंट करता है। chain 20 पर इसमें तीन प्रमाण होते हैं: कॉल, टियर-10 व्यय प्राधिकरण और टियर-14 बंडल।

chain 20 पर शुल्क अब 0.001 + call_fee(tier) नहीं है, जिसके अनुसार chain 15 पर यह कॉल 0.002 RAND की पड़ती थी। न्यूनतम शुल्क BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉ है: 0.001 RAND, उसके साथ गैस मूल्य पर घोषित सीमा (genesis पर 100 इकाइयाँ, एक इकाई 10⁻⁹ RAND के बराबर), और उसके साथ बाइट मूल्य पर प्रूफ़ तथा एनवेलप के बाइट (genesis पर प्रति KiB 800 इकाइयाँ)। chain 20 पर ये दोनों मूल्य 10,000 और 80,000 इकाइयों से ऊपर नहीं जा सकते। सटीक गैस निजी रहती है। डिफ़ॉल्ट रूप से वॉलेट इसे 2^(tier − 2) के गुणज तक ऊपर पूर्णांकित करके घोषित करता है। प्रूफ़ का मूल्य वह chain की 4,194,304-बाइट की सीमा पर लगाता है, क्योंकि शुल्क प्रूफ़ बनने से पहले तय हो जाता है। साथ ही वह गुंजाइश के लिए दो मूल्य-चरण जोड़ता है। chain 18 पर मापी गई चार कॉलों में से प्रत्येक की लागत 0.0057 से 0.0075 RAND रही (उदाहरण प्रोग्राम)। यह कॉल chains 18 से 20 पर नहीं की गई है, इसलिए इसके लिए कोई आँकड़ा नहीं दिया गया है।

chain को टियर, घोषित गैस सीमा, H_IN और आठ आउटपुट शब्द [1, 25, 0, 0, 0, 0, 0, 0] दिखाई देते हैं। शेष राशियाँ उसे कभी नहीं दिखतीं।

रसीदें, इनपुट एनवेलप और rand open-call प्रोग्राम में समझाए गए हैं। इस तरह की Call स्टेटलेस होती है। chain 20 पर कोई प्रोग्राम Invoke के ज़रिए सेल और एक वॉल्ट भी रख सकता है (प्रोग्राम स्टेट (RPL-2))।

दूसरों को अपना hc जाँचने दें

बिल्ड पुनरुत्पादनीय है। build कंपाइलर के परिवेश को साफ़ करता है (RUSTFLAGS, CARGO_* ओवरराइड, RUSTC रैपर और अन्य)। यह cargo कॉन्फ़िग फ़ाइलें स्वीकार नहीं करता और चेकआउट के पथ को /rand-circuits पर रीमैप करता है। Rust 1.98.1 के साथ एक ही स्रोत किसी भी मशीन पर एक ही hc देता है। आपने क्या डिप्लॉय किया, इसकी पुष्टि दूसरे कर सकें, इसके लिए गेस्ट का स्रोत और अपना circuits कमिट प्रकाशित करें (chains 18 से 20 के लिए बने गेस्ट के लिए aeacf31)। फिर कोई भी दोबारा बिल्ड करके hc की तुलना rand_getProgram के code_hash से कर सकता है। chain केवल शब्द संग्रहीत करती है, स्रोत नहीं।

समस्या निवारण

संदेशकारण
… is not inside a circuits checkout (no guest-sdk/ above it)गेस्ट डायरेक्टरी को चेकआउट के भीतर ले जाएँ
build किसी .cargo/config.toml का नाम बताता हैउसे हटाएँ या कहीं और ले जाएँ; उसे साफ़ नहीं किया जा सकता
REJECTED के साथ Capप्रोग्राम --max-words से बड़ा है; इसे लक्ष्य chain की सीमा तक बढ़ाएँ, या प्रोग्राम छोटा करें (opt-level = "s")
REJECTED के साथ Syscall और a7 = 7गेस्ट poseidon2_len को कॉल करता है; aeacf31 पर rand-guest check अभी syscall 7 को नहीं पहचानता (zkp-circuits issue #1)
REJECTED के साथ Fence, Csr या Ebreakबिल्ड ने ऐसा निर्देश उत्पन्न किया जो मशीन में नहीं है; देखें क्या अस्वीकार किया जाता है
run से trap: …रन-टाइम त्रुटि, जैसे असंरेखित पता या अकार्यान्वित syscall संख्या
run बताता है कि रन किसी टियर में नहीं आतारन 2²⁰ पंक्तियों या अपने Poseidon2 बजट से अधिक है; इसे सिद्ध नहीं किया जा सकता

2026-10-12 को fullnode कमिट c0878c3f (चेन 21) के विरुद्ध अंतिम बार सत्यापित, और circuits `7ef3220` (`rand-guest/README.md`, `guest-sdk/`, `guests-compiled/fib`); every `rand-guest` output on this page was produced on 2026-09-19, circuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest/src/check.rs`; `git diff 7ef3220 aeacf31 -- guest-sdk rand-guest`), read on 2026-09-30; no output on this page was re-run at that commit, fullnode tag v0.6.8 `c9c9bd3c` (`docs/deploy.md` on `binding_domain`, `.github/workflows/ci.yml` circuits pin) and `rand_getLimits` on chain 20, read on 2026-10-01; nothing on this page was deployed or called on chain 20, fullnode tag v0.8.0 `c0878c3f` (`CHANGELOG.md`, `docs/zkvm.md` §8a) and `rand_getLimits` on chain 21, read on 2026-10-12; nothing on this page was deployed or called on chain 21। जहाँ कोड और यह पृष्ठ असहमत हों, वहाँ कोड सही है; हमें बताएँ।